diff --git a/static/js/components/app-main/index.js b/static/js/components/app-main/index.js index cb2a65f..a473249 100644 --- a/static/js/components/app-main/index.js +++ b/static/js/components/app-main/index.js @@ -132,7 +132,7 @@ module.exports = Vue.component('app-main', { console.log(xterm); xterm.write(test); - scroll(xtermNode); + //scroll(xtermNode); window.xterm = xterm; @@ -143,12 +143,8 @@ module.exports = Vue.component('app-main', { } } else { runtime.xterm.write(test); - if (runtime.xterm.lines.length > 10) { - runtime.xterm.deleteLines([10]); - runtime.xterm.refresh(0, runtime.xterm.lines.length - 1); - } - scroll(xtermNode); + //scroll(xtermNode); } }, setting: function (){ diff --git a/static/js/terminal/index.js b/static/js/terminal/index.js index 27b1987..e8433a0 100644 --- a/static/js/terminal/index.js +++ b/static/js/terminal/index.js @@ -112,5 +112,6 @@ require('./lib/csi/cursor')(Terminal); require('./lib/csi/repeatPrecedingCharacter')(Terminal); require('./lib/csi/tabClear')(Terminal); require('./lib/csi/softReset')(Terminal); +require('./lib/csi/scroll')(Terminal); require('./lib/charsets.js')(Terminal); diff --git a/static/js/terminal/lib/csi/insert-delete.js b/static/js/terminal/lib/csi/insert-delete.js index 11550d3..e195135 100644 --- a/static/js/terminal/lib/csi/insert-delete.js +++ b/static/js/terminal/lib/csi/insert-delete.js @@ -74,6 +74,44 @@ module.exports = function (Terminal){ this.updateRange(this.scrollBottom); }; + // 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){ + var param = params[0]; + var l = this.ybase + this.rows; + var ch = [this.eraseAttr(), ' ']; // xterm + var 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){ + var param = params[0]; + var l = this.ybase + this.rows; + var ch = [this.eraseAttr(), ' ']; // xterm + var i; + + while (param--) { + for (i = this.ybase; i < l; i++) { + this.lines[i].splice(this.x, 1); + this.lines[i].push(ch); + } + } + + this.maxRange(); + }; + // CSI Ps P // Delete Ps Character(s) (default = 1) (DCH). Terminal.prototype.deleteChars = function (params){ diff --git a/static/js/terminal/lib/csi/scroll.js b/static/js/terminal/lib/csi/scroll.js new file mode 100644 index 0000000..8195e62 --- /dev/null +++ b/static/js/terminal/lib/csi/scroll.js @@ -0,0 +1,33 @@ +/** + * Created by nuintun on 2015/11/25. + */ + +'use strict'; + +module.exports = function (Terminal){ + // CSI Ps S Scroll up Ps lines (default = 1) (SU). + Terminal.prototype.scrollUp = function (params){ + 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.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; + + // CSI Ps T Scroll down Ps lines (default = 1) (SD). + Terminal.prototype.scrollDown = function (params){ + 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.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; +}; diff --git a/static/js/terminal/lib/erase.js b/static/js/terminal/lib/erase.js index 804c1a8..944f49d 100644 --- a/static/js/terminal/lib/erase.js +++ b/static/js/terminal/lib/erase.js @@ -5,6 +5,10 @@ 'use strict'; module.exports = function (Terminal){ + Terminal.prototype.eraseAttr = function (){ + return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); + }; + Terminal.prototype.eraseRight = function (x, y){ var line = this.lines[this.ybase + y]; var ch = [this.curAttr, ' ']; // xterm diff --git a/static/js/terminal/lib/refresh.js b/static/js/terminal/lib/refresh.js index 7340760..1ef3609 100644 --- a/static/js/terminal/lib/refresh.js +++ b/static/js/terminal/lib/refresh.js @@ -16,14 +16,23 @@ module.exports = function (Terminal){ // 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 + // Next 14 bits: a mask for misc. + // flags: 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible Terminal.prototype.refresh = function (start, end){ - var x, y, i, line, out, ch, width, data, attr, fgColor, bgColor, flags, row, parent; + var parent = this.element.parentNode; + var x, y, i, line, out, ch, width, data, attr, fgColor, bgColor, flags, row; + + if (parent && end - start >= this.rows / 2) { + parent.removeChild(this.element); + } width = this.cols; y = start; + if (end >= this.lines.length) { + end = this.lines.length - 1; + } + for (; y <= end; y++) { row = y + this.ydisp; line = this.lines[row]; @@ -65,7 +74,8 @@ module.exports = function (Terminal){ fgColor = (data >> 9) & 0x1ff; flags = data >> 18; - if (flags & 1) { + // bold + if ((flags & 1)) { if (!Terminal.brokenBold) { out += 'font-weight:bold;'; } @@ -74,10 +84,35 @@ module.exports = function (Terminal){ if (fgColor < 8) fgColor += 8; } - if (flags & 2) { + // underline + if ((flags & 2)) { out += 'text-decoration:underline;'; } + // blink + if ((flags & 4)) { + if ((flags & 2)) { + out = out.slice(0, -1); + out += ' blink;'; + } else { + out += 'text-decoration:blink;'; + } + } + + // inverse + if ((flags & 8)) { + bgColor = (data >> 9) & 0x1ff; + fgColor = data & 0x1ff; + // Should inverse just be before the + // above boldColors effect instead? + if ((flags & 1) && fgColor < 8) fgColor += 8; + } + + // invisible + if ((flags & 16)) { + out += 'visibility:hidden;'; + } + if (bgColor !== 256) { out += 'background-color:' + Terminal.colors[bgColor] + ';'; } @@ -105,6 +140,8 @@ module.exports = function (Terminal){ if (ch <= ' ') { out += ' '; } else { + if (this.isWide(ch)) i++; + out += ch; } break; diff --git a/static/js/terminal/lib/scrollDisp.js b/static/js/terminal/lib/scrollDisp.js index 8eb7337..ad644fc 100644 --- a/static/js/terminal/lib/scrollDisp.js +++ b/static/js/terminal/lib/scrollDisp.js @@ -5,6 +5,46 @@ 'use strict'; module.exports = function (Terminal){ + Terminal.prototype.scroll = function (){ + var row; + + if (++this.ybase === this.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.updateRange(this.scrollTop); + this.updateRange(this.scrollBottom); + }; + Terminal.prototype.scrollDisp = function (disp){ this.ydisp += disp; diff --git a/static/js/terminal/lib/util.js b/static/js/terminal/lib/util.js index f27c8d9..6dda23c 100644 --- a/static/js/terminal/lib/util.js +++ b/static/js/terminal/lib/util.js @@ -5,6 +5,18 @@ 'use strict'; module.exports = function (Terminal){ + Terminal.prototype.isWide = function isWide(ch){ + if (ch <= '\uff00') return false; + + return (ch >= '\uff01' && ch <= '\uffbe') + || (ch >= '\uffc2' && ch <= '\uffc7') + || (ch >= '\uffca' && ch <= '\uffcf') + || (ch >= '\uffd2' && ch <= '\uffd7') + || (ch >= '\uffda' && ch <= '\uffdc') + || (ch >= '\uffe0' && ch <= '\uffe6') + || (ch >= '\uffe8' && ch <= '\uffee'); + }; + Terminal.prototype.ch = function (cur){ return cur ? [this.curAttr, ' '] : [this.defAttr, ' ']; }; diff --git a/static/js/terminal/lib/write.js b/static/js/terminal/lib/write.js index 0550966..ab365d4 100644 --- a/static/js/terminal/lib/write.js +++ b/static/js/terminal/lib/write.js @@ -87,7 +87,13 @@ module.exports = function (Terminal){ this.x = 0; } + // TODO: Implement eat_newline_glitch. this.y++; + + if (this.y > this.scrollBottom) { + this.y--; + this.scroll(); + } break; // '\r' case '\r': @@ -128,11 +134,25 @@ module.exports = function (Terminal){ } // FIXME: this prevents errors from being thrown, but needs a proper fix - if (this.lines[this.y + this.ybase]) + if (this.lines[this.y + this.ybase]) { this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; + } this.x++; + this.updateRange(this.y); + + if (this.isWide(ch)) { + var j = this.y + this.ybase; + + if (this.cols < 2 || this.x >= this.cols) { + this.lines[j][this.x - 1] = [this.curAttr, ' ']; + break; + } + + this.lines[j][this.x] = [this.curAttr, ' ']; + this.x++; + } } break; } @@ -453,6 +473,7 @@ module.exports = function (Terminal){ this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; } else if (ch === ';') { this.params.push(this.currentParam); + this.currentParam = ''; } } else { @@ -643,14 +664,14 @@ module.exports = function (Terminal){ break; // CSI Ps S Scroll up Ps lines (default = 1) (SU). case 'S': - //- this.scrollUp(this.params); + 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.params.length < 2 && !this.prefix) { - //- this.scrollDown(this.params); + this.scrollDown(this.params); } break; // CSI Ps Z