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