else
log = -> null
+alternate_charset_table = {
+ A: "↑"
+ B: "↓"
+ C: "→"
+ D: "←"
+ E: "█"
+ F: "▚"
+ G: "☃"
+ '_': " "
+ '`': "◆"
+ a: "▒"
+ b: "␉"
+ c: "␌"
+ d: "␍"
+ e: "␊"
+ f: "°"
+ g: "±"
+ h: ""
+ i: "␋"
+ j: "┘"
+ k: "┐"
+ l: "┌"
+ m: "└"
+ n: "┼"
+ o: "⎺"
+ p: "⎻"
+ q: "─"
+ r: "⎼"
+ s: "⎽"
+ t: "├"
+ u: "┤"
+ v: "┴"
+ w: "┬"
+ x: "│"
+ y: "≤"
+ z: "≥"
+ '{': "π"
+ '|': "≠"
+ '}': "£"
+ '~': "·"
+}
+
class Terminal
# public:
constructor: (width, height) ->
@x -= 1
when '\x0a', '\x0b' # lf, vertical tab (same thing)
@wrap_to_next_line()
+ when '\x0f'
+ @alternate_charset = true
+ when '\x0e'
+ @alternate_charset = false
else
if @x >= @width
@wrap_to_next_line()
+ if @alternate_charset
+ if alternate_charset_table[c]?
+ c = alternate_charset_table[c]
@text[@y][@x] = c
@attributes[@y][@x] = @a
@x += 1
# csi_@: rxvt does nothing I can detect
+ # "The auxiliary keypad keys will send ASCII codes corresponding to the
+ # characters engraved on their keys." (numlock?)
+ "esc_>": ->
+
# move cursor up
csi_A: (lines) ->
lines = parseInt @fix_esc_arg lines, '1'
@x = 0
return
- # set cursor position (one based)
- csi_H: (row, column) ->
- # handle blank/missing args and convert to 0 base
+ # cursor set_row (y)
+ csi_d: (row) ->
row = -1 + parseInt @fix_esc_arg row, '1'
- column = -1 + parseInt @fix_esc_arg column, '1'
+ @set_row_clamped row
+ return
- #clamp values
- if column < 0
- column = 0
- else if column >= @width
- column = @width - 1
+ # cursor column (x)
+ csi_G: (col) ->
+ col = -1 + parseInt @fix_esc_arg col, '1'
+ @set_column_clamped col
+ return
+
+ # set cursor row (zero based)
+ set_row_clamped: (row) ->
if row < 0
row = 0
if row >= @height
row = @height - 1
-
- #move the cursor
- @x = column
@y = row
return
+ # set cursor column (zero based)
+ set_column_clamped: (column) ->
+ if column < 0
+ column = 0
+ if column >= @width
+ column = @width - 1
+ @x = column
+ return
+ # set cursor position (one based)
+ csi_H: (row, column) ->
+ # handle blank/missing args and convert to 0 base
+ row = -1 + parseInt @fix_esc_arg row, '1'
+ column = -1 + parseInt @fix_esc_arg column, '1'
+ @set_row_clamped row
+ @set_column_clamped column
+ return
# clear to screen edge(es)
csi_J: (direction) ->
@text[y][x] = ' '
@attributes[y][x] = 0x07
- # misc
- csiq_h: ->
- args = []
- for i in arguments
+ # delete chars (without moving the cursor)
+ csi_X: (chars) ->
+ chars = parseInt @fix_esc_arg chars, '1'
+ x = @x
+ for c in [0...chars]
+ if x >= @width
+ return
+ @text[@y][x] = ' '
+ @attributes[@y][x] = @a
+ x += 1
+ return
+ "csi_@": (chars) ->
+ chars = parseInt @fix_esc_arg chars, '1'
+ if chars < 1
+ return
+ if chars > @width - @x
+ chars = @width - @x
+ else
+ dest = @width - 1
+ if dest >= @width
+ dest = @width - 1
+ while dest - chars >= @x
+ @text[@y][dest] = @text[@y][dest - chars]
+ @attributes[@y][dest] = @attributes[@y][dest - chars]
+ dest -= 1
+ @csi_X chars # clear
+ csi_P: (chars) ->
+ chars = parseInt @fix_esc_arg chars, '1'
+ if chars < 1
+ return
+ if chars > @width - @x
+ chars = @width - @x
+ else
+ dest = @x
+ while dest < @width - chars
+ @text[@y][dest] = @text[@y][dest + chars]
+ @attributes[@y][dest] = @attributes[@y][dest + chars]
+ dest += 1
+ # clear the space moved out of
+ x = @width - chars
+ while x < @width
+ @text[@y][x] = ' '
+ @attributes[@y][x] = @a
+ x += 1
+
+ # set modes not starting with "?"
+ csi_h: (args...) ->
+ for i in args
arg = @fix_esc_arg i, ''
switch arg
+ when '0' # error (ignored
+ return
+ when '2' # KAM -- keyboard action
+ return
+ when '4' # put cursor in "INSERT" mode
+ return
+ when '12' # turn local echo off (or on?)
+ return
+ # TODO when '20' LNM linefeed/newline
+ else
+ log "Unimplemented arg for csi_h: #{arg}"
+ # set modes starting with "?"
+ csiq_h: (args...) ->
+ for i in args
+ arg = @fix_esc_arg i, ''
+ switch arg
+ when '1' # mode ?1
+ # numlock on
+ return
when '25'
@cursor_visible = true
+ when '1000'
+ # x11 normal mouse tracking
+ return
when '1049'
if @saved_normal_screen?
log "ignoring request to switch to the alt screen because we're already on the alt screen"
return
- @saved_normal_screen = [@x, @y, @text, @attributes]
+ @saved_normal_screen = x: @x, y: @y, text: @text, attributes: @attributes
@text = []
@attributes = []
for y in [0...@height]
@text[y].push ' '
@attributes[y].push 0x07
else
- log "confusing arg for csiq_h: #{arg}"
+ log "Unimplemented arg for csiq_h: #{arg}"
return
- # unmisc
+ # reset (turn off) modes
+ # note that modes starting with ? call csiq_l, not this
+ csi_l: ->
+ for i in arguments
+ arg = @fix_esc_arg i, ''
+ switch arg
+ when '0' # error (ignored
+ return
+ when '2' # KAM -- keyboard action
+ return
+ when '4' # put cursor in "REPLACE" mode
+ return
+ when '12' # turn local echo on (or off?)
+ return
+ # TODO when '20' LNM linefeed/newline
+
+ # reset (turn off) modes starting with ?
+ # in st source, these are priv=true
csiq_l: ->
args = []
for i in arguments
arg = @fix_esc_arg i, ''
switch arg
- when '25'
+ when '1' # mode ?1 reset
+ # numlock off
+ return
+ when '12' # mode ?12
+ # stop blinking the cursor (ignored)
+ return
+ when '25' # mode ?25 reset
@cursor_visible = false
+ when '0', '2', '3', '4', '8', '18', '19', '42', '12'
+ # st ignores all of these
+ return
+ when '9', '1000', '1002', '1003', '1004', '1006'
+ # mouse reporting and such (ignored)
+ return
when '1049'
if not @saved_normal_screen?
log "ignoring request to switch to the normal screen because we're already on the normal screen"
return
- @x = @saved_normal_screen[0]
- @y = @saved_normal_screen[1]
- @text = @saved_normal_screen[2]
- @attributes = @saved_normal_screen[3]
+ @x = @saved_normal_screen.x
+ @y = @saved_normal_screen.y
+ @text = @saved_normal_screen.text
+ @attributes = @saved_normal_screen.attributes
@saved_normal_screen = null
else
- log "confusing arg for csiq_l: #{arg}"
+ log "Unimplemented arg for csiq_l: #{arg}"
return
# set color, bold, underline, etc
# bright bg colors
when '100' # bg bright black
- @set_attribute_bits 0xff, 0x08
+ @set_attribute_bits 0xff00, 0x0800
when '101' # bg bright red
- @set_attribute_bits 0xff, 0x09
+ @set_attribute_bits 0xff00, 0x0900
when '102' # bg bright green
- @set_attribute_bits 0xff, 0x0a
+ @set_attribute_bits 0xff00, 0x0a00
when '103' # bg bright yellow
- @set_attribute_bits 0xff, 0x0b
+ @set_attribute_bits 0xff00, 0x0b00
when '104' # bg bright blue
- @set_attribute_bits 0xff, 0x0c
+ @set_attribute_bits 0xff00, 0x0c00
when '105' # bg bright magenta
- @set_attribute_bits 0xff, 0x0d
+ @set_attribute_bits 0xff00, 0x0d00
when '106' # bg bright cyan
- @set_attribute_bits 0xff, 0x0e
+ @set_attribute_bits 0xff00, 0x0e00
when '107' # bg bright white
- @set_attribute_bits 0xff, 0x0f
+ @set_attribute_bits 0xff00, 0x0f00
else
# if we don't recognize the style, go back to default
@csi_A '1'
else
@csi_L '1'
-
- # str is the whole escape sequence (minus the esc[ prefix)
+
+ # esc ]
+ "esc_]": (seq) ->
+ sem = seq.indexOf ';'
+ if sem < 1 || seq[0] isnt '0'
+ log "Unimplemented esc_] (OS command) sequence: #{seq}"
+ return
+ if window?
+ title = seq.substr sem + 1
+ title = title.substr 0, title.length - 1
+ window.document.title = title
+
+ # "The auxiliary keypad keys will transmit control sequences."
+ "esc_=": ->
+
+ # esc )
+ "esc_)": ->
+ @alternate_charset = true
+ # esc ( 0
+ gzd4_0: ->
+ @alternate_charset = true
+ # esc ( B
+ gzd4_B: ->
+ @alternate_charset = false
+
+ esc_n: ->
+ @alternate_charset = false
+
+ esc_o: ->
+ @alternate_charset = true
+
+ # str is the whole escape sequence (minus the esc character)
update_sequence: (str) ->
if str[0] is '['
- prefix = 'csi_'
if str[1] is '?'
- prefix = 'csiq_'
- str = str.substr 1
- args = str.substr(1, str.length - 2).split ';'
+ method_prefix = 'csiq_'
+ args = str.substr(2, str.length - 3).split ';'
+ else
+ method_prefix = 'csi_'
+ args = str.substr(1, str.length - 2).split ';'
+ method_name = method_prefix + str.substr str.length - 1
+ else if str[0] is '(' # character set
+ method_name = 'gzd4_' + str.substr 1, 1
+ args = [str.substr 1]
else
- prefix = 'esc_'
- args = [str.substr(0, str.length - 1)]
- command = @[prefix + str.substr(str.length - 1)]
- if not command?
- log "Unrecognized sequence: ESC[#{str}"
+ method_name = 'esc_' + str.substr 0, 1
+ args = [str.substr 1]
+ method = @[method_name]
+ if not method?
+ log "Unimplemented #{method_name}, code: ESC#{str}"
return
- command.call this, args...
+ method.apply this, args
update_sequence_then_text: (str) ->
len = @escape_sequence_length str
parts = str.match(/^\[[0-9;?]{0,25}./)
return -1 unless parts?
return parts[0].length
+ else if str[0] is '('
+ if str.length == 1
+ return -1
+ if str[1] is '!' or str[1] is '%'
+ return 3
+ else
+ return 2
+ else if str[0] is ']'
+ st = str.indexOf "\u0007"
+ if st is -1
+ return -1
+ return st + 1
else
- log "non[: ESC#{str.substr 0, 10}"
+ #log "non[: ESC#{str.substr 0, 10}"
if str.length >= 1
return 1
else