diff --git a/bin/16Mtest b/bin/16Mtest
new file mode 100755
index 0000000..6959d24
--- /dev/null
+++ b/bin/16Mtest
@@ -0,0 +1,8 @@
+#!/bin/env python
+import sys
+import os
+rows, cols = map(int, os.popen('stty size', 'r').read().split())
+
+for r in range(rows):
+ for c in range(cols):
+ sys.stdout.write('\x1b[48;2;%d;%d;%dm ' % (255 - r, 255 - c, 255))
diff --git a/butterfly/static/main.js b/butterfly/static/main.js
index fd756ca..31722d6 100644
--- a/butterfly/static/main.js
+++ b/butterfly/static/main.js
@@ -205,6 +205,34 @@
setTimeout(this.resize.bind(this), 100);
}
+ Terminal.prototype.getDefAttr = function() {
+ return {
+ bg: 256,
+ fg: 0,
+ bold: false,
+ underline: false,
+ blink: false,
+ inverse: false,
+ invisible: false
+ };
+ };
+
+ Terminal.prototype.cloneAttr = function(a) {
+ return {
+ bg: a.bg,
+ fg: a.fg,
+ bold: a.bold,
+ underline: a.underline,
+ blink: a.blink,
+ inverse: a.inverse,
+ invisible: a.invisible
+ };
+ };
+
+ Terminal.prototype.equalAttr = function(a, b) {
+ return a.bg === b.bg && a.fg === b.fg && a.bold === b.bold && a.underline === b.underline && a.blink === b.blink && a.inverse === b.inverse && a.invisible === b.invisible;
+ };
+
Terminal.prototype.reset_vars = function() {
var i;
this.x = 0;
@@ -225,8 +253,8 @@
this.gcharset = null;
this.glevel = 0;
this.charsets = [null];
- this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);
- this.curAttr = this.defAttr;
+ this.defAttr = this.getDefAttr();
+ this.curAttr = this.getDefAttr();
this.params = [];
this.currentParam = 0;
this.prefix = "";
@@ -458,7 +486,7 @@
};
Terminal.prototype.refresh = function(start, end) {
- var attr, bg, ch, classes, data, fg, flags, html, i, j, k, l, line, m, out, parent, ref, ref1, ref2, ref3, row, x;
+ var attr, ch, classes, data, fg, html, i, j, k, l, line, m, out, parent, ref, ref1, ref2, ref3, row, styles, x;
if (!this.native_scroll && end - start >= this.rows / 3) {
parent = this.element.parentNode;
if (parent != null) {
@@ -475,43 +503,56 @@
} else {
x = -Infinity;
}
- attr = this.defAttr;
+ attr = this.getDefAttr();
for (i = m = 0, ref2 = this.cols - 1; 0 <= ref2 ? m <= ref2 : m >= ref2; i = 0 <= ref2 ? ++m : --m) {
data = line[i][0];
ch = line[i][1];
- if (data !== attr) {
- if (attr !== this.defAttr) {
+ if (!this.equalAttr(data, attr)) {
+ if (!this.equalAttr(attr, this.getDefAttr())) {
out += "";
}
- if (data !== this.defAttr) {
+ if (!this.equalAttr(data, this.getDefAttr())) {
classes = [];
+ styles = [];
out += "> 9) & 0x1ff;
- flags = data >> 18;
- if (flags & 1) {
+ if (data.bold) {
classes.push("bold");
}
- if (flags & 2) {
+ if (data.underline) {
classes.push("underline");
}
- if (flags & 4) {
+ if (data.blink) {
classes.push("blink");
}
- if (flags & 8) {
+ if (data.inverse) {
classes.push("reverse-video");
}
- if (flags & 16) {
+ if (data.invisible) {
classes.push("invisible");
}
- if (flags & 1 && fg < 8) {
- fg += 8;
+ if (typeof data.fg === 'number') {
+ fg = data.fg;
+ if (data.bold && fg < 8) {
+ fg += 8;
+ }
+ classes.push("fg-color-" + fg);
+ }
+ if (typeof data.fg === 'string') {
+ styles.push("color: " + data.fg);
+ }
+ if (typeof data.bg === 'number') {
+ classes.push("bg-color-" + data.bg);
+ }
+ if (typeof data.bg === 'string') {
+ styles.push("background-color: " + data.bg);
}
- classes.push("bg-color-" + bg);
- classes.push("fg-color-" + fg);
out += "class=\"";
out += classes.join(" ");
- out += "\">";
+ out += "\"";
+ if (styles.length) {
+ out += " style=\"" + styles.join("; ") + "\"";
+ }
+ out += ">";
}
}
if (i === x) {
@@ -548,7 +589,7 @@
}
attr = data;
}
- if (attr !== this.defAttr) {
+ if (!this.equalAttr(attr, this.getDefAttr())) {
out += "";
}
this.children[j].innerHTML = out;
@@ -747,19 +788,19 @@
ch = this.charset[ch];
}
if (this.x >= this.cols) {
- this.screen[this.y + this.ybase][this.x] = [this.curAttr, '\u23CE'];
+ this.screen[this.y + this.ybase][this.x] = [this.cloneAttr(this.curAttr), '\u23CE'];
this.x = 0;
this.next_line();
}
this.updateRange(this.y);
- this.screen[this.y + this.ybase][this.x] = [this.curAttr, ch];
+ this.screen[this.y + this.ybase][this.x] = [this.cloneAttr(this.curAttr), ch];
this.x++;
if (("\uff00" < ch && ch < "\uffef")) {
if (this.cols < 2 || this.x >= this.cols) {
- this.screen[this.y + this.ybase][this.x - 1] = [this.curAttr, " "];
+ this.screen[this.y + this.ybase][this.x - 1] = [this.cloneAttr(this.curAttr), " "];
break;
}
- this.screen[this.y + this.ybase][this.x] = [this.curAttr, " "];
+ this.screen[this.y + this.ybase][this.x] = [this.cloneAttr(this.curAttr), " "];
this.x++;
}
}
@@ -1133,7 +1174,7 @@
this.updateRange(this.y);
} else {
html = "
" + content + "
";
- this.screen[this.y + this.ybase][this.x] = [this.curAttr, html];
+ this.screen[this.y + this.ybase][this.x] = [this.cloneAttr(this.curAttr), html];
line = 0;
while (line < this.get_html_height_in_lines(html) - 1) {
this.y++;
@@ -1895,85 +1936,81 @@
};
Terminal.prototype.charAttributes = function(params) {
- var bg, fg, flags, i, l, p;
+ var i, l, p, results;
if (params.length === 1 && params[0] === 0) {
- this.curAttr = this.defAttr;
+ this.curAttr = this.getDefAttr();
return;
}
- flags = this.curAttr >> 18;
- fg = (this.curAttr >> 9) & 0x1ff;
- bg = this.curAttr & 0x1ff;
l = params.length;
i = 0;
+ results = [];
while (i < l) {
p = params[i];
if (p >= 30 && p <= 37) {
- fg = p - 30;
+ this.curAttr.fg = p - 30;
} else if (p >= 40 && p <= 47) {
- bg = p - 40;
+ this.curAttr.bg = p - 40;
} else if (p >= 90 && p <= 97) {
p += 8;
- fg = p - 90;
+ this.curAttr.fg = p - 90;
} else if (p >= 100 && p <= 107) {
p += 8;
- bg = p - 100;
+ this.curAttr.bg = p - 100;
} else if (p === 0) {
- flags = this.defAttr >> 18;
- fg = (this.defAttr >> 9) & 0x1ff;
- bg = this.defAttr & 0x1ff;
+ this.curAttr = this.getDefAttr();
} else if (p === 1) {
- flags |= 1;
+ this.curAttr.bold = true;
} else if (p === 4) {
- flags |= 2;
+ this.curAttr.underline = true;
} else if (p === 5) {
- flags |= 4;
+ this.curAttr.blink = true;
} else if (p === 7) {
- flags |= 8;
+ this.curAttr.inverse = true;
} else if (p === 8) {
- flags |= 16;
+ this.curAttr.invisible = true;
} else if (p === 10) {
} else if (p === 22) {
- flags &= ~1;
+ this.curAttr.bold = false;
} else if (p === 24) {
- flags &= ~2;
+ this.curAttr.underline = false;
} else if (p === 25) {
- flags &= ~4;
+ this.curAttr.blink = false;
} else if (p === 27) {
- flags &= ~8;
+ this.curAttr.inverse = false;
} else if (p === 28) {
- flags &= ~16;
+ this.curAttr.invisible = false;
} else if (p === 39) {
- fg = (this.defAttr >> 9) & 0x1ff;
+ this.curAttr.fg = 0;
} else if (p === 49) {
- bg = this.defAttr & 0x1ff;
+ this.curAttr.bg = 256;
} else if (p === 38) {
if (params[i + 1] === 2) {
i += 2;
- fg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff;
+ this.curAttr.fg = "rgb(" + params[i] + ", " + params[i + 1] + ", " + params[i + 2] + ")";
i += 2;
} else if (params[i + 1] === 5) {
i += 2;
- fg = params[i] & 0xff;
+ this.curAttr.fg = params[i] & 0xff;
}
} else if (p === 48) {
if (params[i + 1] === 2) {
i += 2;
- bg = "#" + params[i] & 0xff + params[i + 1] & 0xff + params[i + 2] & 0xff;
+ this.curAttr.bg = "rgb(" + params[i] + ", " + params[i + 1] + ", " + params[i + 2] + ")";
i += 2;
} else if (params[i + 1] === 5) {
i += 2;
- bg = params[i] & 0xff;
+ this.curAttr.bg = params[i] & 0xff;
}
} else if (p === 100) {
- fg = (this.defAttr >> 9) & 0x1ff;
- bg = this.defAttr & 0x1ff;
+ this.curAttr.fg = 0;
+ this.curAttr.bg = 256;
} else {
console.error("Unknown SGR attribute: %d.", p);
}
- i++;
+ results.push(i++);
}
- return this.curAttr = (flags << 18) | (fg << 9) | bg;
+ return results;
};
Terminal.prototype.deviceStatus = function(params) {
diff --git a/coffees/term.coffee b/coffees/term.coffee
index aa0bf80..7b91ce6 100644
--- a/coffees/term.coffee
+++ b/coffees/term.coffee
@@ -118,6 +118,29 @@ class Terminal
setTimeout(@resize.bind(@), 100)
+ getDefAttr: ->
+ bg: 256
+ fg: 0
+ bold: false
+ underline: false
+ blink: false
+ inverse: false
+ invisible: false
+
+ cloneAttr: (a) ->
+ bg: a.bg
+ fg: a.fg
+ bold: a.bold
+ underline: a.underline
+ blink: a.blink
+ inverse: a.inverse
+ invisible: a.invisible
+
+ equalAttr: (a, b) ->
+ (a.bg is b.bg and a.fg is b.fg and a.bold is b.bold and
+ a.underline is b.underline and a.blink is b.blink and
+ a.inverse is b.inverse and a.invisible is b.invisible)
+
reset_vars: ->
@x = 0
@y = 0
@@ -144,8 +167,9 @@ class Terminal
@charsets = [null]
# stream
- @defAttr = (0 << 18) | (257 << 9) | (256 << 0)
- @curAttr = @defAttr
+ @defAttr = @getDefAttr()
+
+ @curAttr = @getDefAttr()
@params = []
@currentParam = 0
@prefix = ""
@@ -397,37 +421,50 @@ class Terminal
else
x = -Infinity
- attr = @defAttr
+ attr = @getDefAttr()
for i in [0..@cols - 1]
data = line[i][0]
ch = line[i][1]
- if data isnt attr
- out += "" if attr isnt @defAttr
- if data isnt @defAttr
+ unless @equalAttr data, attr
+ out += "" unless @equalAttr attr, @getDefAttr()
+ unless @equalAttr data, @getDefAttr()
classes = []
+ styles = []
out += "> 9) & 0x1ff
- flags = data >> 18
# bold
- classes.push "bold" if flags & 1
+ classes.push "bold" if data.bold
# underline
- classes.push "underline" if flags & 2
+ classes.push "underline" if data.underline
# blink
- classes.push "blink" if flags & 4
+ classes.push "blink" if data.blink
# inverse
- classes.push "reverse-video" if flags & 8
+ classes.push "reverse-video" if data.inverse
# invisible
- classes.push "invisible" if flags & 16
+ classes.push "invisible" if data.invisible
- fg += 8 if flags & 1 and fg < 8
- classes.push "bg-color-" + bg
- classes.push "fg-color-" + fg
+ if typeof data.fg is 'number'
+ fg = data.fg
+ if data.bold and fg < 8
+ fg += 8
+ classes.push "fg-color-" + fg
+
+ if typeof data.fg is 'string'
+ styles.push "color: " + data.fg
+
+ if typeof data.bg is 'number'
+ classes.push "bg-color-" + data.bg
+
+ if typeof data.bg is 'string'
+ styles.push "background-color: " + data.bg
out += "class=\""
out += classes.join(" ")
- out += "\">"
+ out += "\""
+ if styles.length
+ out += " style=\"" + styles.join("; ") + "\""
+ out += ">"
+
out += "" if i is x
@@ -453,7 +490,7 @@ class Terminal
out += ch
out += "" if i is x
attr = data
- out += "" if attr isnt @defAttr
+ out += "" unless @equalAttr attr, @getDefAttr()
@children[j].innerHTML = out
parent?.appendChild @element
@@ -625,20 +662,20 @@ class Terminal
if ch >= " "
ch = @charset[ch] if @charset?[ch]
if @x >= @cols
- @screen[@y + @ybase][@x] = [@curAttr, '\u23CE']
+ @screen[@y + @ybase][@x] = [@cloneAttr(@curAttr), '\u23CE']
@x = 0
@next_line()
@updateRange @y
- @screen[@y + @ybase][@x] = [@curAttr, ch]
+ @screen[@y + @ybase][@x] = [@cloneAttr(@curAttr), ch]
@x++
if "\uff00" < ch < "\uffef"
if @cols < 2 or @x >= @cols
- @screen[@y + @ybase][@x - 1] = [@curAttr, " "]
+ @screen[@y + @ybase][@x - 1] = [@cloneAttr(@curAttr), " "]
break
- @screen[@y + @ybase][@x] = [@curAttr, " "]
+ @screen[@y + @ybase][@x] = [@cloneAttr(@curAttr), " "]
@x++
when State.escaped
@@ -1079,7 +1116,7 @@ class Terminal
else
html = "" + content + "
"
@screen[@y + @ybase][@x] = [
- @curAttr
+ @cloneAttr(@curAttr)
html
]
line = 0
@@ -1778,7 +1815,7 @@ class Terminal
# 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 4 -> Not underline.
# Ps = 2 5 -> Steady (not blinking).
# Ps = 2 7 -> Positive (not inverse).
# Ps = 2 8 -> Visible, i.e., not hidden (VT300).
@@ -1835,113 +1872,96 @@ class Terminal
charAttributes: (params) ->
# Optimize a single SGR0.
if params.length is 1 and params[0] is 0
- @curAttr = @defAttr
+ @curAttr = @getDefAttr()
return
- flags = @curAttr >> 18
-
- fg = (@curAttr >> 9) & 0x1ff
- bg = @curAttr & 0x1ff
-
l = params.length
i = 0
while i < l
p = params[i]
if p >= 30 and p <= 37
# fg color 8
- fg = p - 30
+ @curAttr.fg = p - 30
else if p >= 40 and p <= 47
# bg color 8
- bg = p - 40
+ @curAttr.bg = p - 40
else if p >= 90 and p <= 97
# fg color 16
p += 8
- fg = p - 90
+ @curAttr.fg = p - 90
else if p >= 100 and p <= 107
# bg color 16
p += 8
- bg = p - 100
+ @curAttr.bg = p - 100
else if p is 0
# default
- flags = @defAttr >> 18
- fg = (@defAttr >> 9) & 0x1ff
- bg = @defAttr & 0x1ff
-
- # flags = 0;
- # fg = 0x1ff;
- # bg = 0x1ff;
+ @curAttr = @getDefAttr()
else if p is 1
# bold text
- flags |= 1
+ @curAttr.bold = true
else if p is 4
- # underlined text
- flags |= 2
+ # underline text
+ @curAttr.underline = true
else if p is 5
# blink
- flags |= 4
+ @curAttr.blink = true
else if p is 7
# inverse and positive
# test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
- flags |= 8
+ @curAttr.inverse = true
else if p is 8
# invisible
- flags |= 16
+ @curAttr.invisible = true
else if p is 10
# Primary Font
# ignoring
else if p is 22
# not bold
- flags &= ~1
+ @curAttr.bold = false
else if p is 24
- # not underlined
- flags &= ~2
+ # not underline
+ @curAttr.underline = false
else if p is 25
# not blink
- flags &= ~4
+ @curAttr.blink = false
else if p is 27
# not inverse
- flags &= ~8
+ @curAttr.inverse = false
else if p is 28
# not invisible
- flags &= ~16
+ @curAttr.invisible = false
else if p is 39
# reset fg
- fg = (@defAttr >> 9) & 0x1ff
+ @curAttr.fg = 0
else if p is 49
# reset bg
- bg = @defAttr & 0x1ff
+ @curAttr.bg = 256
else if p is 38
if params[i + 1] is 2
# fg color 2^24
i += 2
- fg = "#" + params[i] & 0xff +
- params[i + 1] & 0xff +
- params[i + 2] & 0xff
+ @curAttr.fg = "rgb(#{params[i]}, #{params[i+1]}, #{params[i+2]})"
i += 2
else if params[i + 1] is 5
# fg color 256
i += 2
- fg = params[i] & 0xff
+ @curAttr.fg = params[i] & 0xff
else if p is 48
if params[i + 1] is 2
# bg color 2^24
i += 2
- bg = "#" + params[i] & 0xff +
- params[i + 1] & 0xff +
- params[i + 2] & 0xff
+ @curAttr.bg = "rgb(#{params[i]}, #{params[i+1]}, #{params[i+2]})"
i += 2
else if params[i + 1] is 5
# bg color 256
i += 2
- bg = params[i] & 0xff
+ @curAttr.bg = params[i] & 0xff
else if p is 100
# reset fg/bg
- fg = (@defAttr >> 9) & 0x1ff
- bg = @defAttr & 0x1ff
+ @curAttr.fg = 0
+ @curAttr.bg = 256
else
console.error "Unknown SGR attribute: %d.", p
i++
- @curAttr = (flags << 18) | (fg << 9) | bg
-
# CSI Ps n Device Status Report (DSR).
# Ps = 5 -> Status Report. Result (``OK'') is