|
|
|
@ -5,257 +5,477 @@ class AsciiIo.VT
|
|
|
|
|
@data = ''
|
|
|
|
|
@resetTerminal()
|
|
|
|
|
@render()
|
|
|
|
|
@compilePatterns()
|
|
|
|
|
# @compilePatterns()
|
|
|
|
|
|
|
|
|
|
handleData: (data) ->
|
|
|
|
|
if data.match(/^\x1b[\x00-\x1f]/)
|
|
|
|
|
@handleControlCharacter(data[1])
|
|
|
|
|
return 2
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^(\x1b\x5d|\x9d).*?(\x1b\\\\|\x9c|\x07)/)
|
|
|
|
|
# OSC seq
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^(\x1b[PX_^]|[\x90\x98\x9e\x9f]).*?(\x1b\\\\|\x9c)/)
|
|
|
|
|
# DCS/SOS/PM/APC seq
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^(?:\x1b\x5b|\x9b)([\x30-\x3f]*?)[\x20-\x2f]*?[\x40-\x7e]/)
|
|
|
|
|
# Control sequences
|
|
|
|
|
@handleControlSequence(match[0], match[1], match)
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x30-\x3f]/)
|
|
|
|
|
@handlePrivateEscSeq(match[0])
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^\x1b[\x20-\x2f]*?[\x40-\x5a\x5c-\x7e]/) # excluding \x5b "["
|
|
|
|
|
@handleStandardEscSeq(match[0])
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if data.match(/^\x1b\x7f/) # DELETE
|
|
|
|
|
return 2
|
|
|
|
|
|
|
|
|
|
else if data.match(/^[\x00-\x1a\x1c-\x1f]/) # excluding \x1b "ESC"
|
|
|
|
|
@handleControlCharacter(data[0])
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
else if match = data.match(/^([\x20-\x7e]|\xe2..|[\xa1-\xfe])+/)
|
|
|
|
|
@handlePrintableCharacters(match[0])
|
|
|
|
|
return match[0].length
|
|
|
|
|
|
|
|
|
|
else if data[0] is "\x7f"
|
|
|
|
|
# DELETE, always and everywhere ignored
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
else if data.match(/^[\x80-\x9f]/)
|
|
|
|
|
@handleControlCharacter(data[0])
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
else if data[0] is "\xa0"
|
|
|
|
|
# Same as SPACE (\x20)
|
|
|
|
|
@handlePrintableCharacters(' ')
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
else if data[0] is "\xff"
|
|
|
|
|
# Same as DELETE (\x7f)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
handleControlCharacter: (char) ->
|
|
|
|
|
switch char
|
|
|
|
|
when "\x07"
|
|
|
|
|
@bell()
|
|
|
|
|
when "\x08"
|
|
|
|
|
@backspace()
|
|
|
|
|
when "\x09"
|
|
|
|
|
@buffer.goToNextHorizontalTabStop()
|
|
|
|
|
# @tab()
|
|
|
|
|
when "\x0a"
|
|
|
|
|
@lineFeed()
|
|
|
|
|
when "\x0b"
|
|
|
|
|
@verticalTab()
|
|
|
|
|
when "\x0c"
|
|
|
|
|
@formFeed()
|
|
|
|
|
when "\x0d"
|
|
|
|
|
@carriageReturn()
|
|
|
|
|
|
|
|
|
|
when "\x84"
|
|
|
|
|
@index() # "D"
|
|
|
|
|
when "\x85"
|
|
|
|
|
@newLine() # "E"
|
|
|
|
|
when "\x88"
|
|
|
|
|
@setHorizontalTabStop() # "H"
|
|
|
|
|
when "\x8d"
|
|
|
|
|
@reverseIndex() # "M"
|
|
|
|
|
|
|
|
|
|
noop: ->
|
|
|
|
|
|
|
|
|
|
# handleC0ControlSet: (data) ->
|
|
|
|
|
# console.log 'handling C0'
|
|
|
|
|
|
|
|
|
|
handlePrintableCharacters: (text) ->
|
|
|
|
|
@buffer.print text
|
|
|
|
|
|
|
|
|
|
handleStandardEscSeq: (data) ->
|
|
|
|
|
last = data[data.length - 1]
|
|
|
|
|
|
|
|
|
|
if last.match(/[\x40-\x5f]/)
|
|
|
|
|
# convert to C1
|
|
|
|
|
char = String.fromCharCode(last.charCodeAt(0) + 0x40)
|
|
|
|
|
@handleControlCharacter(char)
|
|
|
|
|
else
|
|
|
|
|
switch last
|
|
|
|
|
when "c"
|
|
|
|
|
@resetTerminal()
|
|
|
|
|
|
|
|
|
|
handlePrivateEscSeq: (data) ->
|
|
|
|
|
last = data[data.length - 1]
|
|
|
|
|
|
|
|
|
|
switch last
|
|
|
|
|
when "7"
|
|
|
|
|
@saveTerminalState()
|
|
|
|
|
when "8"
|
|
|
|
|
@restoreTerminalState()
|
|
|
|
|
|
|
|
|
|
handleControlSequence: (data, params, match) ->
|
|
|
|
|
if params and params.match(/^[\x3c-\x3f]/)
|
|
|
|
|
@handlePrivateControlSequence(data, params)
|
|
|
|
|
else
|
|
|
|
|
@handleStandardControlSequence(data, params)
|
|
|
|
|
|
|
|
|
|
handleStandardControlSequence: (data, params) ->
|
|
|
|
|
term = data[data.length - 1]
|
|
|
|
|
|
|
|
|
|
numbers = @parseParams(params)
|
|
|
|
|
n = numbers[0]
|
|
|
|
|
m = numbers[1]
|
|
|
|
|
|
|
|
|
|
# handleC1ControlSet: (data) ->
|
|
|
|
|
|
|
|
|
|
# handleControlSequence: (data) ->
|
|
|
|
|
|
|
|
|
|
_C0_PATTERNS:
|
|
|
|
|
# C0 set of 7-bit control characters
|
|
|
|
|
"[\x00-\x1f]":
|
|
|
|
|
# bell
|
|
|
|
|
"\x07": (data) -> @bell()
|
|
|
|
|
|
|
|
|
|
# backspace
|
|
|
|
|
"\x08": (data) -> @backspace()
|
|
|
|
|
|
|
|
|
|
# Move the cursor to the next tab stop
|
|
|
|
|
"\x09": (data) ->
|
|
|
|
|
|
|
|
|
|
"\x0a": (data) -> @lineFeed()
|
|
|
|
|
|
|
|
|
|
"\x0d": (data) -> @carriageReturn()
|
|
|
|
|
|
|
|
|
|
"\x0e": (data) ->
|
|
|
|
|
|
|
|
|
|
"\x0f": (data) ->
|
|
|
|
|
|
|
|
|
|
# Reserved (?)
|
|
|
|
|
"\x82": (data) ->
|
|
|
|
|
|
|
|
|
|
# Cancel Character, ignore previous character
|
|
|
|
|
"\x94": (data) ->
|
|
|
|
|
|
|
|
|
|
# Escape sequence
|
|
|
|
|
"\x1b": _.extend({}, @_C0_PATTERNS, {
|
|
|
|
|
|
|
|
|
|
# Control sequence
|
|
|
|
|
"\x1b\\[": @_CS_PATTERNS
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
_CS_PATTERNS:
|
|
|
|
|
"sth": 1
|
|
|
|
|
|
|
|
|
|
_PATTERNS: _.extend({}, @_C0_PATTERNS, {
|
|
|
|
|
# Printable characters
|
|
|
|
|
"([\x20-\x7e])+": @handlePrintableCharacters
|
|
|
|
|
|
|
|
|
|
# "Delete", always and everywhere ignored
|
|
|
|
|
"[\x7f\xff]": @noop
|
|
|
|
|
|
|
|
|
|
# C1 control set
|
|
|
|
|
"[\x80-\x9f]":
|
|
|
|
|
|
|
|
|
|
# Control sequence
|
|
|
|
|
"\x9b": @_CS_PATTERNS
|
|
|
|
|
|
|
|
|
|
# G1 Displayable, 94 additional displayable characters
|
|
|
|
|
"[\xa1-\xfe]": @handlePrintableCharacters
|
|
|
|
|
|
|
|
|
|
# Always and everywhere a blank space
|
|
|
|
|
"\xa0": -> @handlePrintableCharacters(' ')
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
PATTERNS:
|
|
|
|
|
"\x00": (data) ->
|
|
|
|
|
"\x07": (data) -> @bell()
|
|
|
|
|
"\x08": (data) -> @backspace()
|
|
|
|
|
"\x09": (data) -> @buffer.goToNextHorizontalTabStop()
|
|
|
|
|
"\x0a": (data) -> @lineFeed()
|
|
|
|
|
"\x0b": (data) -> @verticalTab()
|
|
|
|
|
"\x0c": (data) -> @formFeed()
|
|
|
|
|
"\x0d": (data) -> @carriageReturn()
|
|
|
|
|
"\x0e": (data) ->
|
|
|
|
|
"\x0f": (data) ->
|
|
|
|
|
"\x82": (data) -> # Reserved (?)
|
|
|
|
|
"\x85": (data) -> @setHorizontalTabStop()
|
|
|
|
|
"\x94": (data) -> # Cancel Character, ignore previous character
|
|
|
|
|
|
|
|
|
|
# 20 - 7e
|
|
|
|
|
"([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
|
|
|
|
|
@buffer.print match[0]
|
|
|
|
|
|
|
|
|
|
"\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
|
|
|
|
|
|
|
|
|
|
"\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
|
|
|
|
|
if match[1].length == 0
|
|
|
|
|
@params = []
|
|
|
|
|
else
|
|
|
|
|
@params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
|
|
|
|
|
|
|
|
|
|
@n = @params[0]
|
|
|
|
|
@m = @params[1]
|
|
|
|
|
@handleCS match[2]
|
|
|
|
|
|
|
|
|
|
# private standards
|
|
|
|
|
"\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
|
|
|
|
|
# h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
|
|
|
|
|
# l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
|
|
|
|
|
# 1001 + s = ?
|
|
|
|
|
# 1001 + r = ?
|
|
|
|
|
modes = match[1].split(";")
|
|
|
|
|
action = match[2]
|
|
|
|
|
mode = undefined
|
|
|
|
|
|
|
|
|
|
for mode in modes
|
|
|
|
|
if mode is "1"
|
|
|
|
|
# 1 + h / l = cursor keys stuff
|
|
|
|
|
else if mode is "5"
|
|
|
|
|
# Reverse/normal video - ignoring
|
|
|
|
|
else if mode is "7"
|
|
|
|
|
# Enables/disables autowrap mode
|
|
|
|
|
else if mode is "12"
|
|
|
|
|
if action is "h"
|
|
|
|
|
# blinking cursor
|
|
|
|
|
else action is "l"
|
|
|
|
|
# steady cursor
|
|
|
|
|
else if mode is "25"
|
|
|
|
|
if action is "h"
|
|
|
|
|
@showCursor()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
@hideCursor()
|
|
|
|
|
else if mode is "47"
|
|
|
|
|
if action is "h"
|
|
|
|
|
@switchToAlternateBuffer()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
@switchToNormalBuffer()
|
|
|
|
|
else if mode is "1000"
|
|
|
|
|
# Enables/disables normal mouse tracking
|
|
|
|
|
else if mode is "1001"
|
|
|
|
|
# pbly sth with mouse/keys...
|
|
|
|
|
else if mode is "1002"
|
|
|
|
|
# 2002 + h / l = mouse tracking stuff
|
|
|
|
|
else if mode is "1049"
|
|
|
|
|
if action is "h"
|
|
|
|
|
# Save cursor position, switch to alternate screen buffer, and clear screen.
|
|
|
|
|
@switchToAlternateBuffer()
|
|
|
|
|
@clearScreen()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
# Clear screen, switch to normal screen buffer, and restore cursor position.
|
|
|
|
|
@clearScreen()
|
|
|
|
|
@switchToNormalBuffer()
|
|
|
|
|
else
|
|
|
|
|
throw "unknown mode: " + mode + action
|
|
|
|
|
|
|
|
|
|
"\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
|
|
|
|
|
|
|
|
|
|
"\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
|
|
|
|
|
|
|
|
|
|
"\x1b\\\\": -> # String Terminator (VT125 exits graphics)
|
|
|
|
|
|
|
|
|
|
"\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
|
|
|
|
|
|
|
|
|
|
"\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
|
|
|
|
|
|
|
|
|
|
"\x1bc": -> @resetTerminal()
|
|
|
|
|
"\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
|
|
|
|
|
"\x1bD": -> @index()
|
|
|
|
|
"\x1bE": -> @newLine()
|
|
|
|
|
"\x1bH": -> @setHorizontalTabStop()
|
|
|
|
|
"\x1bM": -> @reverseIndex()
|
|
|
|
|
"\x1bk": -> # NAPLPS lock-shift G1 to GR
|
|
|
|
|
|
|
|
|
|
"\x1b7": (data) -> # save cursor pos and char attrs
|
|
|
|
|
@saveTerminalState()
|
|
|
|
|
|
|
|
|
|
"\x1b8": (data) -> # restore cursor pos and char attrs
|
|
|
|
|
@restoreTerminalState()
|
|
|
|
|
|
|
|
|
|
handleCS: (term) ->
|
|
|
|
|
switch term
|
|
|
|
|
when "@"
|
|
|
|
|
@buffer.insertCharacters @n
|
|
|
|
|
@buffer.insertCharacters n
|
|
|
|
|
when "A"
|
|
|
|
|
@buffer.priorRow @n
|
|
|
|
|
@buffer.priorRow n
|
|
|
|
|
when "B"
|
|
|
|
|
@buffer.nextRow @n
|
|
|
|
|
@buffer.nextRow n
|
|
|
|
|
when "C"
|
|
|
|
|
@buffer.nextColumn @n
|
|
|
|
|
@buffer.nextColumn n
|
|
|
|
|
when "D"
|
|
|
|
|
@buffer.priorColumn @n
|
|
|
|
|
@buffer.priorColumn n
|
|
|
|
|
when "E"
|
|
|
|
|
@buffer.nextRowFirstColumn @n
|
|
|
|
|
@buffer.nextRowFirstColumn n
|
|
|
|
|
when "F"
|
|
|
|
|
@buffer.priorRowFirstColumn @n
|
|
|
|
|
@buffer.priorRowFirstColumn n
|
|
|
|
|
when "G"
|
|
|
|
|
@buffer.goToColumn @n
|
|
|
|
|
@buffer.goToColumn n
|
|
|
|
|
when "H"
|
|
|
|
|
@buffer.goToRowAndColumn @n, @m
|
|
|
|
|
@buffer.goToRowAndColumn n, m
|
|
|
|
|
when "I"
|
|
|
|
|
@buffer.goToNextHorizontalTabStop @n
|
|
|
|
|
@buffer.goToNextHorizontalTabStop n
|
|
|
|
|
when "J"
|
|
|
|
|
if @n is 2
|
|
|
|
|
if n is 2
|
|
|
|
|
@buffer.eraseScreen()
|
|
|
|
|
else if @n is 1
|
|
|
|
|
else if n is 1
|
|
|
|
|
@buffer.eraseFromScreenStart()
|
|
|
|
|
else
|
|
|
|
|
@buffer.eraseToScreenEnd()
|
|
|
|
|
when "K"
|
|
|
|
|
if @n is 2
|
|
|
|
|
if n is 2
|
|
|
|
|
@buffer.eraseRow()
|
|
|
|
|
else if @n is 1
|
|
|
|
|
else if n is 1
|
|
|
|
|
@buffer.eraseFromRowStart()
|
|
|
|
|
else
|
|
|
|
|
@buffer.eraseToRowEnd()
|
|
|
|
|
when "L"
|
|
|
|
|
@buffer.insertLine @n or 1
|
|
|
|
|
@buffer.insertLine n or 1
|
|
|
|
|
when "M"
|
|
|
|
|
@buffer.deleteLine @n or 1
|
|
|
|
|
@buffer.deleteLine n or 1
|
|
|
|
|
when "P" # DCH - Delete Character, from current position to end of field
|
|
|
|
|
@buffer.deleteCharacters @n or 1
|
|
|
|
|
@buffer.deleteCharacters n or 1
|
|
|
|
|
when "S"
|
|
|
|
|
@buffer.scrollUp @n
|
|
|
|
|
@buffer.scrollUp n
|
|
|
|
|
when "T"
|
|
|
|
|
@buffer.scrollDown @n
|
|
|
|
|
@buffer.scrollDown n
|
|
|
|
|
when "X"
|
|
|
|
|
@buffer.eraseCharacters @n
|
|
|
|
|
@buffer.eraseCharacters n
|
|
|
|
|
when "Z"
|
|
|
|
|
@buffer.goToPriorHorizontalTabStop @n
|
|
|
|
|
@buffer.goToPriorHorizontalTabStop n
|
|
|
|
|
when "b"
|
|
|
|
|
@buffer.repeatLastCharacter @n
|
|
|
|
|
@buffer.repeatLastCharacter n
|
|
|
|
|
when "d" # VPA - Vertical Position Absolute
|
|
|
|
|
@buffer.goToRow @n
|
|
|
|
|
@buffer.goToRow n
|
|
|
|
|
when "f"
|
|
|
|
|
@buffer.goToRowAndColumn @n, @m
|
|
|
|
|
@buffer.goToRowAndColumn n, m
|
|
|
|
|
when "g"
|
|
|
|
|
if !@n or @n is 0
|
|
|
|
|
if !n or n is 0
|
|
|
|
|
@buffer.clearHorizontalTabStop()
|
|
|
|
|
else if @n is 3
|
|
|
|
|
else if n is 3
|
|
|
|
|
@buffer.clearAllHorizontalTabStops()
|
|
|
|
|
when "l" # l, Reset mode
|
|
|
|
|
console.log "(TODO) reset: " + @n
|
|
|
|
|
console.log "(TODO) reset: " + n
|
|
|
|
|
when "m"
|
|
|
|
|
@handleSGR @params
|
|
|
|
|
@handleSGR numbers
|
|
|
|
|
when "n"
|
|
|
|
|
@reportRowAndColumn()
|
|
|
|
|
when "r" # Set top and bottom margins (scroll region on VT100)
|
|
|
|
|
@setScrollRegion @n or 0, @m or @lines - 1
|
|
|
|
|
when "^"
|
|
|
|
|
# reserved
|
|
|
|
|
# Privacy Message (password verification), terminated by ST
|
|
|
|
|
# TODO
|
|
|
|
|
@setScrollRegion n or 0, m or @lines - 1
|
|
|
|
|
else
|
|
|
|
|
throw "no handler for CSI term: " + term
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handlePrivateControlSequence: (data, params) ->
|
|
|
|
|
action = data[data.length - 1]
|
|
|
|
|
modes = @parseParams(params)
|
|
|
|
|
|
|
|
|
|
console.log "private: #{@formattedData(data)}, action: #{action}, modes: #{modes}"
|
|
|
|
|
for mode in modes
|
|
|
|
|
if mode is 25
|
|
|
|
|
if action is "h"
|
|
|
|
|
@showCursor()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
@hideCursor()
|
|
|
|
|
else if mode is 47
|
|
|
|
|
if action is "h"
|
|
|
|
|
@switchToAlternateBuffer()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
@switchToNormalBuffer()
|
|
|
|
|
else if mode is 1049
|
|
|
|
|
if action is "h"
|
|
|
|
|
# Save cursor position, switch to alternate screen buffer, and clear screen.
|
|
|
|
|
console.log 'switching'
|
|
|
|
|
@switchToAlternateBuffer()
|
|
|
|
|
@clearScreen()
|
|
|
|
|
else if action is "l"
|
|
|
|
|
# Clear screen, switch to normal screen buffer, and restore cursor position.
|
|
|
|
|
console.log 'switching back'
|
|
|
|
|
@clearScreen()
|
|
|
|
|
@switchToNormalBuffer()
|
|
|
|
|
# else
|
|
|
|
|
# throw "unknown mode: " + mode + action
|
|
|
|
|
|
|
|
|
|
parseParams: (params) ->
|
|
|
|
|
if params.length is 0
|
|
|
|
|
numbers = []
|
|
|
|
|
else
|
|
|
|
|
numbers = _(params.replace(/[^0-9;]/, '').split(';')).map (n) -> if n is '' then undefined else parseInt(n)
|
|
|
|
|
|
|
|
|
|
numbers
|
|
|
|
|
|
|
|
|
|
# _C0_PATTERNS:
|
|
|
|
|
# default: (data) ->
|
|
|
|
|
# console.log "no handler for: #{@formattedData(data)}"
|
|
|
|
|
|
|
|
|
|
# # C0 set of 7-bit control characters
|
|
|
|
|
# "\x1b?[\x00-\x1f]":
|
|
|
|
|
# # bell
|
|
|
|
|
# "\x07": (data) -> @bell()
|
|
|
|
|
|
|
|
|
|
# # backspace
|
|
|
|
|
# "\x08": (data) -> @backspace()
|
|
|
|
|
|
|
|
|
|
# # Move the cursor to the next tab stop
|
|
|
|
|
# "\x09": (data) ->
|
|
|
|
|
|
|
|
|
|
# "\x0a": (data) -> @lineFeed()
|
|
|
|
|
|
|
|
|
|
# "\x0d": (data) -> @carriageReturn()
|
|
|
|
|
|
|
|
|
|
# "\x0e": (data) ->
|
|
|
|
|
|
|
|
|
|
# "\x0f": (data) ->
|
|
|
|
|
|
|
|
|
|
# # Reserved (?)
|
|
|
|
|
# "\x82": (data) ->
|
|
|
|
|
|
|
|
|
|
# # Cancel Character, ignore previous character
|
|
|
|
|
# "\x94": (data) ->
|
|
|
|
|
|
|
|
|
|
# # # Escape sequence
|
|
|
|
|
# # "\x1b": _.extend({}, @_C0_PATTERNS, {
|
|
|
|
|
|
|
|
|
|
# # Control sequence
|
|
|
|
|
# "\x1b\\[": @_CS_PATTERNS
|
|
|
|
|
|
|
|
|
|
# _CS_PATTERNS:
|
|
|
|
|
# "sth": 1
|
|
|
|
|
|
|
|
|
|
# _PATTERNS: _.extend({}, @_C0_PATTERNS, {
|
|
|
|
|
# # Printable characters
|
|
|
|
|
# "([\x20-\x7e])+": @handlePrintableCharacters
|
|
|
|
|
|
|
|
|
|
# # "Delete", always and everywhere ignored
|
|
|
|
|
# "[\x7f\xff]": @noop
|
|
|
|
|
|
|
|
|
|
# # C1 control set
|
|
|
|
|
# "[\x80-\x9f]":
|
|
|
|
|
|
|
|
|
|
# # Control sequence
|
|
|
|
|
# "\x9b": @_CS_PATTERNS
|
|
|
|
|
|
|
|
|
|
# # G1 Displayable, 94 additional displayable characters
|
|
|
|
|
# "[\xa1-\xfe]": @handlePrintableCharacters
|
|
|
|
|
|
|
|
|
|
# # Always and everywhere a blank space
|
|
|
|
|
# "\xa0": -> @handlePrintableCharacters(' ')
|
|
|
|
|
|
|
|
|
|
# })
|
|
|
|
|
|
|
|
|
|
# PATTERNS:
|
|
|
|
|
# "\x00": (data) ->
|
|
|
|
|
# "\x07": (data) -> @bell()
|
|
|
|
|
# "\x08": (data) -> @backspace()
|
|
|
|
|
# "\x09": (data) -> @buffer.goToNextHorizontalTabStop()
|
|
|
|
|
# "\x0a": (data) -> @lineFeed()
|
|
|
|
|
# "\x0b": (data) -> @verticalTab()
|
|
|
|
|
# "\x0c": (data) -> @formFeed()
|
|
|
|
|
# "\x0d": (data) -> @carriageReturn()
|
|
|
|
|
# "\x0e": (data) ->
|
|
|
|
|
# "\x0f": (data) ->
|
|
|
|
|
|
|
|
|
|
# # 20 - 7e
|
|
|
|
|
# "([\x20-\x7e]|\xe2..|[\xc2\xc4\xc5].)+": (data, match) ->
|
|
|
|
|
# @buffer.print match[0]
|
|
|
|
|
|
|
|
|
|
# "\x1b\\(B": (data) -> # SCS (Set G0 Character SET)
|
|
|
|
|
|
|
|
|
|
# "\x1b\\[([0-9;]*)([\x40-\x7e])": (data, match) ->
|
|
|
|
|
# if match[1].length == 0
|
|
|
|
|
# @params = []
|
|
|
|
|
# else
|
|
|
|
|
# @params = _(match[1].split(';')).map (n) -> if n is '' then undefined else parseInt(n)
|
|
|
|
|
|
|
|
|
|
# @n = @params[0]
|
|
|
|
|
# @m = @params[1]
|
|
|
|
|
# @handleCS match[2]
|
|
|
|
|
|
|
|
|
|
# # private standards
|
|
|
|
|
# "\x1b\\[\\?([\x30-\x3f]+)([hlsr])": (data, match) ->
|
|
|
|
|
# # h = Sets DEC/xterm specific mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#decmode)
|
|
|
|
|
# # l = Resets mode (http://ttssh2.sourceforge.jp/manual/en/about/ctrlseq.html#mode)
|
|
|
|
|
# # 1001 + s = ?
|
|
|
|
|
# # 1001 + r = ?
|
|
|
|
|
# modes = match[1].split(";")
|
|
|
|
|
# action = match[2]
|
|
|
|
|
# mode = undefined
|
|
|
|
|
|
|
|
|
|
# for mode in modes
|
|
|
|
|
# if mode is "1"
|
|
|
|
|
# # 1 + h / l = cursor keys stuff
|
|
|
|
|
# else if mode is "5"
|
|
|
|
|
# # Reverse/normal video - ignoring
|
|
|
|
|
# else if mode is "7"
|
|
|
|
|
# # Enables/disables autowrap mode
|
|
|
|
|
# else if mode is "12"
|
|
|
|
|
# if action is "h"
|
|
|
|
|
# # blinking cursor
|
|
|
|
|
# else action is "l"
|
|
|
|
|
# # steady cursor
|
|
|
|
|
# else if mode is "25"
|
|
|
|
|
# if action is "h"
|
|
|
|
|
# @showCursor()
|
|
|
|
|
# else if action is "l"
|
|
|
|
|
# @hideCursor()
|
|
|
|
|
# else if mode is "47"
|
|
|
|
|
# if action is "h"
|
|
|
|
|
# @switchToAlternateBuffer()
|
|
|
|
|
# else if action is "l"
|
|
|
|
|
# @switchToNormalBuffer()
|
|
|
|
|
# else if mode is "1000"
|
|
|
|
|
# # Enables/disables normal mouse tracking
|
|
|
|
|
# else if mode is "1001"
|
|
|
|
|
# # pbly sth with mouse/keys...
|
|
|
|
|
# else if mode is "1002"
|
|
|
|
|
# # 2002 + h / l = mouse tracking stuff
|
|
|
|
|
# else if mode is "1049"
|
|
|
|
|
# if action is "h"
|
|
|
|
|
# # Save cursor position, switch to alternate screen buffer, and clear screen.
|
|
|
|
|
# @switchToAlternateBuffer()
|
|
|
|
|
# @clearScreen()
|
|
|
|
|
# else if action is "l"
|
|
|
|
|
# # Clear screen, switch to normal screen buffer, and restore cursor position.
|
|
|
|
|
# @clearScreen()
|
|
|
|
|
# @switchToNormalBuffer()
|
|
|
|
|
# else
|
|
|
|
|
# throw "unknown mode: " + mode + action
|
|
|
|
|
|
|
|
|
|
# "\x1b\x3d": (data) -> # DECKPAM - Set keypad to applications mode (ESCape instead of digits)
|
|
|
|
|
|
|
|
|
|
# "\x1b\x3e": (data) -> # DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq)
|
|
|
|
|
|
|
|
|
|
# "\x1b\\\\": -> # String Terminator (VT125 exits graphics)
|
|
|
|
|
|
|
|
|
|
# "\x1b\x5d[012]\x3b.*?\x07": (data, match) -> # OSC - Operating System Command (terminal title)
|
|
|
|
|
|
|
|
|
|
# "\x1b\\[>c": (data) -> # Secondary Device Attribute request (?)
|
|
|
|
|
|
|
|
|
|
# "\x1bc": -> @resetTerminal()
|
|
|
|
|
# "\x1bP([^\\\\])*?\\\\": (data) -> # DCS, Device Control String
|
|
|
|
|
# "\x1bD": -> @index()
|
|
|
|
|
# "\x1bE": -> @newLine()
|
|
|
|
|
# "\x1bH": -> @setHorizontalTabStop()
|
|
|
|
|
# "\x1bM": -> @reverseIndex()
|
|
|
|
|
# "\x1bk": -> # NAPLPS lock-shift G1 to GR
|
|
|
|
|
|
|
|
|
|
# "\x1b7": (data) -> # save cursor pos and char attrs
|
|
|
|
|
# @saveTerminalState()
|
|
|
|
|
|
|
|
|
|
# "\x1b8": (data) -> # restore cursor pos and char attrs
|
|
|
|
|
# @restoreTerminalState()
|
|
|
|
|
|
|
|
|
|
# handleCS: (term) ->
|
|
|
|
|
# switch term
|
|
|
|
|
# when "@"
|
|
|
|
|
# @buffer.insertCharacters @n
|
|
|
|
|
# when "A"
|
|
|
|
|
# @buffer.priorRow @n
|
|
|
|
|
# when "B"
|
|
|
|
|
# @buffer.nextRow @n
|
|
|
|
|
# when "C"
|
|
|
|
|
# @buffer.nextColumn @n
|
|
|
|
|
# when "D"
|
|
|
|
|
# @buffer.priorColumn @n
|
|
|
|
|
# when "E"
|
|
|
|
|
# @buffer.nextRowFirstColumn @n
|
|
|
|
|
# when "F"
|
|
|
|
|
# @buffer.priorRowFirstColumn @n
|
|
|
|
|
# when "G"
|
|
|
|
|
# @buffer.goToColumn @n
|
|
|
|
|
# when "H"
|
|
|
|
|
# @buffer.goToRowAndColumn @n, @m
|
|
|
|
|
# when "I"
|
|
|
|
|
# @buffer.goToNextHorizontalTabStop @n
|
|
|
|
|
# when "J"
|
|
|
|
|
# if @n is 2
|
|
|
|
|
# @buffer.eraseScreen()
|
|
|
|
|
# else if @n is 1
|
|
|
|
|
# @buffer.eraseFromScreenStart()
|
|
|
|
|
# else
|
|
|
|
|
# @buffer.eraseToScreenEnd()
|
|
|
|
|
# when "K"
|
|
|
|
|
# if @n is 2
|
|
|
|
|
# @buffer.eraseRow()
|
|
|
|
|
# else if @n is 1
|
|
|
|
|
# @buffer.eraseFromRowStart()
|
|
|
|
|
# else
|
|
|
|
|
# @buffer.eraseToRowEnd()
|
|
|
|
|
# when "L"
|
|
|
|
|
# @buffer.insertLine @n or 1
|
|
|
|
|
# when "M"
|
|
|
|
|
# @buffer.deleteLine @n or 1
|
|
|
|
|
# when "P" # DCH - Delete Character, from current position to end of field
|
|
|
|
|
# @buffer.deleteCharacters @n or 1
|
|
|
|
|
# when "S"
|
|
|
|
|
# @buffer.scrollUp @n
|
|
|
|
|
# when "T"
|
|
|
|
|
# @buffer.scrollDown @n
|
|
|
|
|
# when "X"
|
|
|
|
|
# @buffer.eraseCharacters @n
|
|
|
|
|
# when "Z"
|
|
|
|
|
# @buffer.goToPriorHorizontalTabStop @n
|
|
|
|
|
# when "b"
|
|
|
|
|
# @buffer.repeatLastCharacter @n
|
|
|
|
|
# when "d" # VPA - Vertical Position Absolute
|
|
|
|
|
# @buffer.goToRow @n
|
|
|
|
|
# when "f"
|
|
|
|
|
# @buffer.goToRowAndColumn @n, @m
|
|
|
|
|
# when "g"
|
|
|
|
|
# if !@n or @n is 0
|
|
|
|
|
# @buffer.clearHorizontalTabStop()
|
|
|
|
|
# else if @n is 3
|
|
|
|
|
# @buffer.clearAllHorizontalTabStops()
|
|
|
|
|
# when "l" # l, Reset mode
|
|
|
|
|
# console.log "(TODO) reset: " + @n
|
|
|
|
|
# when "m"
|
|
|
|
|
# @handleSGR @params
|
|
|
|
|
# when "n"
|
|
|
|
|
# @reportRowAndColumn()
|
|
|
|
|
# when "r" # Set top and bottom margins (scroll region on VT100)
|
|
|
|
|
# @setScrollRegion @n or 0, @m or @lines - 1
|
|
|
|
|
# when "^"
|
|
|
|
|
# # reserved
|
|
|
|
|
# # Privacy Message (password verification), terminated by ST
|
|
|
|
|
# # TODO
|
|
|
|
|
# else
|
|
|
|
|
# throw "no handler for CSI term: " + term
|
|
|
|
|
|
|
|
|
|
handleSGR: (numbers) ->
|
|
|
|
|
@buffer.setBrush @sgrInterpreter.buildBrush(@buffer.brush, numbers)
|
|
|
|
|
|
|
|
|
@ -263,35 +483,50 @@ class AsciiIo.VT
|
|
|
|
|
@view.visualBell()
|
|
|
|
|
# @trigger('bell')
|
|
|
|
|
|
|
|
|
|
compilePatterns: ->
|
|
|
|
|
@COMPILED_PATTERNS = ([new RegExp("^" + re), f] for re, f of @PATTERNS)
|
|
|
|
|
# compilePatterns: ->
|
|
|
|
|
# @COMPILED_PATTERNS = ([new RegExp("^" + re), f] for re, f of @PATTERNS)
|
|
|
|
|
|
|
|
|
|
feed: (data) ->
|
|
|
|
|
@data += data
|
|
|
|
|
|
|
|
|
|
while @data.length > 0
|
|
|
|
|
match = null
|
|
|
|
|
processed = @handleData(@data)
|
|
|
|
|
|
|
|
|
|
for pattern in @COMPILED_PATTERNS
|
|
|
|
|
match = pattern[0].exec(@data)
|
|
|
|
|
if processed is 0
|
|
|
|
|
# console.log "no kurwa: #{@formattedData(@data)}"
|
|
|
|
|
break
|
|
|
|
|
# throw 'up'
|
|
|
|
|
|
|
|
|
|
if match
|
|
|
|
|
handler = pattern[1]
|
|
|
|
|
handler.call(this, @data, match)
|
|
|
|
|
@data = @data.slice(match[0].length)
|
|
|
|
|
break
|
|
|
|
|
@data = @data.slice(processed)
|
|
|
|
|
|
|
|
|
|
break unless match
|
|
|
|
|
# match = null
|
|
|
|
|
|
|
|
|
|
@render()
|
|
|
|
|
# for pattern in @COMPILED_PATTERNS
|
|
|
|
|
# match = pattern[0].exec(@data)
|
|
|
|
|
|
|
|
|
|
if @data.length > 500
|
|
|
|
|
head = @data.slice(0, 100)
|
|
|
|
|
hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
|
|
|
|
|
console.log "failed matching: '" + Utf8.decode(head) + "' (" + hex.join() + ")" # [pos: " + (@dataIndex - count) + "]"
|
|
|
|
|
# if match
|
|
|
|
|
# handler = pattern[1]
|
|
|
|
|
# handler.call(this, @data, match)
|
|
|
|
|
# @data = @data.slice(match[0].length)
|
|
|
|
|
# break
|
|
|
|
|
|
|
|
|
|
# break unless match
|
|
|
|
|
|
|
|
|
|
@render()
|
|
|
|
|
|
|
|
|
|
@data.length is 0
|
|
|
|
|
|
|
|
|
|
# aaa: ->
|
|
|
|
|
# if @data.length > 500
|
|
|
|
|
# head = @data.slice(0, 100)
|
|
|
|
|
# hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
|
|
|
|
|
# console.log "failed matching: '" + Utf8.decode(head) + "' (" + hex.join() + ")" # [pos: " + (@dataIndex - count) + "]"
|
|
|
|
|
|
|
|
|
|
formattedData: (data) ->
|
|
|
|
|
head = data.slice(0, 100)
|
|
|
|
|
hex = ("0x#{c.charCodeAt(0).toString(16)}" for c in head)
|
|
|
|
|
Utf8.decode(head) + " (" + hex.join() + ")"
|
|
|
|
|
|
|
|
|
|
render: ->
|
|
|
|
|
@view.render(@buffer.changes(), @buffer.cursorX, @buffer.cursorY)
|
|
|
|
|
@buffer.clearChanges()
|
|
|
|
@ -391,3 +626,4 @@ class AsciiIo.VT
|
|
|
|
|
reportRowAndColumn: ->
|
|
|
|
|
|
|
|
|
|
# http://www.shaels.net/index.php/propterm/documents
|
|
|
|
|
# http://manpages.ubuntu.com/manpages/lucid/man7/urxvt.7.html
|
|
|
|
|