From 346e39b72382df8272b875a010ff2af78cf1c2ea Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 21 Jul 2013 23:53:10 -0500 Subject: [PATCH] cleanup. improve BCE. remove old code. --- lib/colors.js | 80 +++++++++++++++++++ lib/program.js | 36 ++++++++- lib/widget.js | 212 ++++++++++++++++++++++--------------------------- 3 files changed, 207 insertions(+), 121 deletions(-) diff --git a/lib/colors.js b/lib/colors.js index efb1c69..f45d87d 100644 --- a/lib/colors.js +++ b/lib/colors.js @@ -62,6 +62,86 @@ exports.matchColor = function(hex) { return exports._cache[hex] = li; }; +exports.matchColor_ = function(r1, g1, b1) { + if (typeof r1 === 'string' && g1 == null) { + var hex = r1; + if (hex[0] !== '#') { + return hex; + } + if (hex.length === 4) { + hex = hex[0] + + hex[1] + hex[1] + + hex[2] + hex[2] + + hex[3] + hex[3]; + } + hex = exports.HexToRGB(hex); + r1 = hex[0], g1 = hex[1], b1 = hex[2]; + } + + var hash = r1 + '.' + g1 + '.' + b1; + + if (exports._cache[hash] != null) { + return exports._cache[hash]; + } + + var ldiff = Infinity + , li = -1 + , i = 0 + , c + , r2 + , g2 + , b2 + , diff; + + for (; i < exports.vcolors.length; i++) { + c = exports.vcolors[i]; + r2 = c[0]; + g2 = c[1]; + b2 = c[2]; + + if (r1 === r2 && g1 === g2 && b1 === b2) { + diff = 0; + } else { + // diff = colorDistance3d(r1, g1, b1, r2, g2, b2); + diff = colorDistance3dWeight(r1, g1, b1, r2, g2, b2); + // diff = colorDistance3dWeightSqrt(r1, g1, b1, r2, g2, b2); + } + + if (diff === 0) { + li = i; + break; + } + + if (diff < ldiff) { + ldiff = diff; + li = i; + } + } + + return exports._cache[hash] = li; +}; + +exports.RGBToHex = function(r, g, b) { + if (Array.isArray(r)) b = r[2], g = r[1], r = r[0]; + + function hex(n) { + n = n.toString(16); + if (n.length < 2) n = '0' + n; + return n; + } + + return '#' + hex(r) + hex(g) + hex(b); +}; + +exports.hexToRGB = function(hex) { + var col = parseInt(hex.substring(1), 16) + , r = (col >> 16) & 0xff + , g = (col >> 8) & 0xff + , b = col & 0xff; + + return [r, g, b]; +}; + // As it happens, comparing how similar two colors are is really hard. Here is // one of the simplest solutions, which doesn't require conversion to another // color space, posted on stackoverflow. Maybe someone better at math can diff --git a/lib/program.js b/lib/program.js index 7d722d5..bf7c444 100644 --- a/lib/program.js +++ b/lib/program.js @@ -1155,7 +1155,7 @@ Program.prototype._parseChar = function(text, attr) { this.x = 0; } else { this.x++; - if (this.x >= this.width) { + if (this.x >= this.cols) { this._wrapCursor(); } } @@ -1318,7 +1318,7 @@ Program.prototype.return = function() { Program.prototype.nel = Program.prototype.newline = Program.prototype.feed = function() { - if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.width) { + if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.cols) { return; } this.x = 0; @@ -1383,7 +1383,8 @@ Program.prototype.tabSet = function() { // ESC 7 Save Cursor (DECSC). Program.prototype.sc = -Program.prototype.saveCursor = function() { +Program.prototype.saveCursor = function(key) { + if (key) return this.lsaveCursor(key); this.savedX = this.x || 0; this.savedY = this.y || 0; if (this.tput) return this.put.sc(); @@ -1392,13 +1393,40 @@ Program.prototype.saveCursor = function() { // ESC 8 Restore Cursor (DECRC). Program.prototype.rc = -Program.prototype.restoreCursor = function() { +Program.prototype.restoreCursor = function(key, hide) { + if (key) return this.lrestoreCursor(key, hide); this.x = this.savedX || 0; this.y = this.savedY || 0; if (this.tput) return this.put.rc(); return this.esc('8'); }; +// Save Cursor Locally +Program.prototype.lsaveCursor = function(key) { + var key = key || 'local'; + this._saved = this._saved || {}; + this._saved[key] = this._saved[key] || {}; + this._saved[key].x = this.x; + this._saved[key].y = this.y; + this._saved[key].hidden = this.cursorHidden; +}; + +// Restore Cursor Locally +Program.prototype.lrestoreCursor = function(key, hide) { + var key = key || 'local', pos; + if (!this._saved || !this._saved[key]) return; + pos = this._saved[key]; + delete this._saved[key]; + this.cup(pos.y, pos.x); + if (hide && pos.hidden !== this.cursorHidden) { + if (pos.hidden) { + this.hideCursor(); + } else { + this.showCursor(); + } + } +}; + // ESC # 3 DEC line height/width Program.prototype.lineHeight = function() { return this.esc('#'); diff --git a/lib/widget.js b/lib/widget.js index 5f825b7..37196ee 100644 --- a/lib/widget.js +++ b/lib/widget.js @@ -826,17 +826,17 @@ Screen.prototype.draw = function(start, end) { , attr , fg , bg - , flags - , acs - , bc; + , flags; + + var clr + , neq + , xx; var lx = -1 , ly = -1 , o; - var bcx = -1 - , bcy = -1 - , bca = -1; + var acs; // var cx = this.program.x // , cy = this.program.y @@ -844,7 +844,8 @@ Screen.prototype.draw = function(start, end) { // // if (!ch) this.program.hideCursor(); - this.program.saveCursor(); + // this.program.sc('draw'); + this.program.sc(); for (y = start; y <= end; y++) { line = this.lines[y]; @@ -861,63 +862,75 @@ Screen.prototype.draw = function(start, end) { data = line[x][0]; ch = line[x][1]; - // Take advantage of xterm's back_color_erase - // feature by using a lookahead. - if (this.tput.bools.back_color_erase && ch === ' ' && line[x + 1]) { - var ne = false - , cl = true; + // Take advantage of xterm's back_color_erase feature by using a + // lookahead. Stop spitting out so many damn spaces. NOTE: Is checking + // the bg for non BCE terminals worth the overhead? + if (ch === ' ' + && ((this.tput && this.tput.bools.back_color_erase) + || (data & 0x1ff) === (this.dattr & 0x1ff)) + && ((data >> 18) & 8) === ((this.dattr >> 18) & 8)) { + clr = true; + neq = false; - for (var xi = x; xi < this.cols; xi++) { - if (line[xi][0] !== data || line[xi][1] !== ' ') { - cl = false; + for (xx = x; xx < this.cols; xx++) { + if (line[xx][0] !== data || line[xx][1] !== ' ') { + clr = false; break; } - if (line[xi][0] !== o[xi][0] || line[xi][1] !== o[xi][1]) { - ne = true; + if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) { + neq = true; } } - if (cl && ne) { + if (clr && neq) { if (data !== attr) { out += this.codeAttr(data); attr = data; } out += this.tput.el(0); - for (var xi = x; xi < this.cols; xi++) { - o[xi][0] = data; - o[xi][1] = ' '; + for (xx = x; xx < this.cols; xx++) { + o[xx][0] = data; + o[xx][1] = ' '; } break; } - // comment the break in the first xi loop above for this to work. - // if (!ne) { + // If there's more than 10 spaces, use EL regardless + // and start over drawing the rest of line. Might + // not be worth it. + // if (!clr && neq && (xx - x) > 10) { + // if (data !== attr) { + // out += this.codeAttr(data); + // attr = data; + // } + // out += this.tput.el(0) + this.tput.cuf(xx - x); + // clr = xx; // tmp + // for (xx = x; xx < this.cols; xx++) { + // o[xx][0] = data; + // o[xx][1] = ' '; + // } + // x = clr - 1; + // continue; + // } + + // Skip to the next line if the + // rest of the line is already drawn. + // Comment the break in the first + // xx loop above for this to work. + // Which carries less overhead? + // OR use another for loop: + // for (xx = x; xx < this.cols; xx++) { + // if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) { + // neq = true; + // break; + // } + // } + // if (!neq) { // attr = data; // break; // } } - // Take advantage of xterm's back_color_erase feature. - // Stop spitting out so many damn spaces. - if (0) if (this.tput.bools.back_color_erase) { - if (ch === ' ' && (bca === -1 || bca === data)) { - if (bcy === -1) { - bcx = x; - bcy = y; - bca = data; - } - //ch = ''; - } else if (bcy !== -1) { - // If it's more than ten spaces, just EL and jump forward. - // Might break things since it could alter the output buffer. - // out += x - bcx > 10 - // ? this.tput.el(0) + this.tput.cuf(x - bcx) - // : Array((x - bcx) + 1).join(' '); - out += Array((x - bcx) + 1).join(' '); - bcx = -1, bcy = -1, bca = -1; - } - } - // Optimize by comparing the real output // buffer to the pending output buffer. if (data === o[x][0] && ch === o[x][1]) { @@ -1013,7 +1026,6 @@ Screen.prototype.draw = function(start, end) { } } -/* // Attempt to use ACS for supported characters. // This is not ideal, but it's how ncurses works. // There are a lot of terminals that support ACS @@ -1028,13 +1040,22 @@ Screen.prototype.draw = function(start, end) { // Maybe remove !this.tput.unicode check, however, // this seems to be the way ncurses does it. if (this.tput) { - if (this.tput.strings.enter_alt_charset_mode - && this.tput.acscr[ch] - && (!this.tput.brokenACS || !this.tput.unicode)) { - ch = this.tput.smacs() - + this.tput.acscr[ch] - + this.tput.rmacs(); - } else if (!this.tput.strings.enter_alt_charset_mode) { + if (this.tput.strings.enter_alt_charset_mode) { + if (!this.tput.brokenACS || !this.tput.unicode) { + if (this.tput.acscr[ch]) { + if (acs) { + ch = this.tput.acscr[ch]; + } else { + ch = this.tput.smacs() + + this.tput.acscr[ch] + acs = true; + } + } else if (acs) { + ch = this.tput.rmacs() + ch; + acs = false; + } + } + } else { // U8 is not consistently correct. Some terminfo's // terminals that do not declare it may actually // support utf8 (e.g. urxvt), but if the terminal @@ -1042,61 +1063,22 @@ Screen.prototype.draw = function(start, end) { // are it does not support UTF8. This is probably // the "safest" way to do this. Should fix things // like sun-color. + // if (this.program.term('sun') && ch > '~') { + // if (this.tput.numbers.U8 !== 1 && ch > '~') { if (this.tput.numbers.U8 !== 1 && this.utoa[ch]) { ch = this.tput.utoa[ch] || '?'; } } - // } else if (this.program.term('sun') && ch > '~') { - // } else if (this.tput.numbers.U8 !== 1 && ch > '~') { - // // If this terminal doesn't explicitly support UTF8, - // // replace unicode characters with ugly shitty ones. - // ch = this.tput.utoa[ch] || '?'; - // } } -*/ - - if (this.tput.strings.enter_alt_charset_mode) { - if (!this.tput.brokenACS || !this.tput.unicode) { - if (this.tput.acscr[ch]) { - if (acs) { - ch = this.tput.acscr[ch]; - } else { - ch = this.tput.smacs() - + this.tput.acscr[ch] - acs = true; - } - } else if (acs) { - ch = this.tput.rmacs() + ch; - acs = false; - } - } - } else { - // if (this.program.term('sun') && ch > '~') { - // if (this.tput.numbers.U8 !== 1 && ch > '~') { - if (this.tput.numbers.U8 !== 1 && this.utoa[ch]) { - ch = this.tput.utoa[ch] || '?'; - } - } - - if (0) if (bcy !== -1) ch = ''; out += ch; attr = data; } - if (0) if (bcy !== -1) { - out += this.tput.el(0); - bcx = -1, bcy = -1, bca = -1; - } - if (attr !== this.dattr) { out += '\x1b[m'; } - //if (this.tput.bools.back_color_erase && out && lx === -1) { - // out = this._replaceSpaces(out); - //} - if (this.tput) { if (out) this.program.write(this.tput.cup(y, 0) + out); } else { @@ -1107,28 +1089,8 @@ Screen.prototype.draw = function(start, end) { // this.program.cup(cy, cx); // if (ch) this.program.hideCursor(); - this.program.restoreCursor(); -}; - -Screen.prototype._replaceSpaces = function(out) { - if (!this._sr) this._sr = this._spaceReplacer.bind(this); - return out.replace(/ {5,}/g, this._sr); -}; - -Screen.prototype._spaceReplacer = function(ch) { - return this.tput.el(0) + this.tput.cuf(ch.length); -}; - -Screen.prototype._replaceSpaces = function(out) { - if (!this._sr) this._sr = this._spaceReplacer.bind(this); - // / +(?=(?:\x1b\[m)?(?:\x1b\[[\d;]*H|$))/g, - return out.replace( - / {5,}(?=(?:\x1b\[[\d;]*m)*(?:\x1b\[[\d;]*H|$))/g, - this._sr); -}; - -Screen.prototype._spaceReplacer = function(ch) { - return this.tput.el(0); + // this.program.rc('draw', true); + this.program.rc(); }; Screen.prototype._reduceColor = function(col) { @@ -1196,15 +1158,31 @@ Screen.prototype.attrCode = function(code, cur) { case 28: flags &= ~16; break; + case 100: // default fg/bg + bg = 0x1ff; + fg = 0x1ff; + break; default: // color - if (c === 48 && code[i+1] === '5') { + if (c === 48 && +code[i+1] === 5) { i += 2; bg = +code[i]; break; - } else if (c === 38 && code[i+1] === '5') { + } else if (c === 48 && +code[i+1] === 2) { + i += 2; + bg = colors.matchColor( + colors.RGBToHex(+code[i], +code[i+1], +code[i+2])); + i += 2; + break; + } else if (c === 38 && +code[i+1] === 5) { i += 2; fg = +code[i]; break; + } else if (c === 38 && +code[i+1] === 2) { + i += 2; + fg = colors.matchColor( + colors.RGBToHex(+code[i], +code[i+1], +code[i+2])); + i += 2; + break; } if (c >= 40 && c <= 47) { bg = c - 40;