+++ /dev/null
-handler = (req, res) ->
- err = (req, res) ->
- res.writeHead(404, 'Content-Type': 'text/plain')
- return res.end("Error loading #{req.url}")
- switch req.url
- when '/', '/index.html'
- filename = __dirname + '/index.html'
- type = 'text/html'
- when '/jquery.js'
- filename = '/usr/share/javascript/jquery/jquery.min.js'
- type = 'text/javascript'
- else
- return err req, res
- fs.readFile filename, (err, data) ->
- return err req, res if err
-
- res.writeHead(200, 'Content-Type': type)
- res.end(data)
-
-app = require('http').createServer(handler)
-io = require('socket.io').listen(app)
-fs = require('fs')
-terminal = require('./terminal.coffee')
-
-# SETTINGS
-app.listen(9293)
-term = terminal.new(105, 66)
-
-io.sockets.on 'connection', (socket) ->
- # FIXME socket.emit 'write', term.getState()
- term.on 'sequence', (data) ->
- socket.emit 'write', "sequence: #{data}"
- term.on 'text', (data) ->
- socket.emit 'write', data
- socket.on 'disconnect', ->
- # FIXME stop term update callback
- console.log 'client disconnected'
-
-process.stdin.resume()
-process.stdin.setEncoding 'utf8'
-
-process.stdin.on 'data', (data) ->
- term.update data
-
-process.stdin.on 'end', -> process.exit()
--- /dev/null
+handler = (req, res) ->
+ err = (req, res) ->
+ res.writeHead(404, 'Content-Type': 'text/plain')
+ return res.end("Error loading #{req.url}")
+ switch req.url
+ when '/', '/index.html'
+ filename = __dirname + '/index.html'
+ type = 'text/html'
+ when '/jquery.js'
+ filename = '/usr/share/javascript/jquery/jquery.min.js'
+ type = 'text/javascript'
+ else
+ return err req, res
+ fs.readFile filename, (err, data) ->
+ return err req, res if err
+
+ res.writeHead(200, 'Content-Type': type)
+ res.end(data)
+
+app = require('http').createServer(handler)
+io = require('socket.io').listen(app)
+fs = require('fs')
+terminal = require('./terminal.coffee')
+
+# SETTINGS
+app.listen(9293)
+term = terminal.new(105, 66)
+
+sockets = []
+
+io.sockets.on 'connection', (socket) ->
+ sockets.push socket
+ socket.on 'disconnect', ->
+ for i in [i...sockets.length]
+ if sockets[i] is socket
+ sockets.splice i, 1
+ return
+
+process.stdin.resume()
+process.stdin.setEncoding 'utf8'
+
+process.stdin.on 'data', (data) ->
+ term.update data
+ for s in sockets
+ s.emit 'init', width: term.width, height: term.height, x: term.x, y: term.y, a: term.a, text: term.text, attributes: term.attributes
+
+process.stdin.on 'end', -> process.exit()
constructor: (width, height) ->
@width = 1
@height = 1
- @text = [""]
- @attributes = [0]
+ @text = []
+ @attributes = []
@x = 0
@y = 0
+ @a = 0 # cursor attributes
@partial = ''
@resize width, height
- @text_callbacks = []
- @sequence_callbacks = []
- on: (evt, callback) ->
- @["#{evt}_callbacks"].push callback
-
resize: (width, height) ->
# FIXME: write a version that retains some of the data
+ @width = width
+ @height = height
@text = []
@attributes = []
- for i in [0...height]
- @text[i] = ""
- @attributes[i] = new Array(width)
+ for y in [0...height]
+ @text[y] = []
+ @attributes[y] = []
+ for x in [0...width]
+ @text[y].push ' '
+ @attributes[y].push 0
# pass data from stdout
update: (data) ->
else
@update_sequence_then_text parts[i]
return
+
+ clear_rest_of_line: ->
+ for i in [@x...@width]
+ @text[@y][i] = ' '
+ @attributes[@y][i] = @a
+
+ add_new_line: ->
+ # clear top line
+ for i in [0...@width]
+ @text[0][i] = ' '
+ @attributes[0][i] = 0
+ # move (newly cleared) top line to the bottom
+ tmp = @text.shift()
+ @text.push(tmp)
+ tmp = @attributes.shift()
+ @attributes.push(tmp)
+ # slide cursor up with rest of text
+ @y -= 1
+
+ wrap_to_next_line: ->
+ if @y is @height - 1
+ @add_new_line()
+ @y += 1
+ @x = 0
# str has no escape sequences
update_text: (str) ->
return unless str.length > 0
- for c in @text_callbacks
- c str
- console.log "text: \"#{str}\"" # FIXME
+ for c in str
+ switch c
+ when '\t' # tab
+ @update_text " ".substr(@x % 8)
+ when '\x07' # bell
+ false
+ when '\x0d' # cr
+ @x = 0
+ when '\x08' # backspace
+ if @x > 0
+ @x -= 1
+ @text[@y][@x] = ' '
+ # should this set the attribute too?
+ when '\x0a', '\x0b' # lf, vertical tab (same thing)
+ @wrap_to_next_line()
+ else
+ @text[@y][@x] = c
+ @attributes[@y][@x] = @a
+ @x += 1
+ if @x is @width
+ @wrap_to_next_line()
+ return
+
+ set_attribute_bits: (mask, value) ->
+ @a = (@a & ~mask) | value
+
+ csi_m: default: "0", go: ->
+ for i in arguments
+ switch i
+ when '0'
+ @set_attribute_bits 0xffffff, 0
+ when '1' # bold
+ @set_attribute_bits 0x100, 1
+ when '4' # underline
+ @set_attribute_bits 0x200, 1
+ when '5' # blink
+ @set_attribute_bits 0x400, 1
+ when '8' # invisible
+ @set_attribute_bits 0x800, 1
+
+ when '22' # not bold... according to a page
+ @set_attribute_bits 0x100, 0
+ when '21' # ... though this would make more sense for "not bold"
+ @set_attribute_bits 0x100, 0
+ when '24' # not underline
+ @set_attribute_bits 0x200, 0
+ when '25' # not blink
+ @set_attribute_bits 0x400, 0
+ when '28' # not invisible
+ @set_attribute_bits 0x800, 0
+
+ when '30' # fg black
+ @set_attribute_bits 0xff, 0
+ when '31' # fg red
+ @set_attribute_bits 0xff, 0xe0
+ when '32' # fg green
+ @set_attribute_bits 0xff, 0x1c
+ when '33' # fg yellow
+ @set_attribute_bits 0xff, 0xfc
+ when '34' # fg blue
+ @set_attribute_bits 0xff, 0x02
+ when '35' # fg magenta
+ @set_attribute_bits 0xff, 0xe2
+ when '36' # fg cyan
+ @set_attribute_bits 0xff, 0x1f
+ when '37', '39' # fg white (39 is default)
+ @set_attribute_bits 0xff, 0xff
+
+ when '40' # bg black
+ @set_attribute_bits 0xff00, 0
+ when '41' # bg red
+ @set_attribute_bits 0xff00, 0xe000
+ when '42' # bg green
+ @set_attribute_bits 0xff00, 0x1c00
+ when '43' # bg yellow
+ @set_attribute_bits 0xff00, 0xfc00
+ when '44' # bg blue
+ @set_attribute_bits 0xff00, 0x0200
+ when '45' # bg magenta
+ @set_attribute_bits 0xff00, 0xe200
+ when '46' # bg cyan
+ @set_attribute_bits 0xff00, 0x1f00
+ when '47', '49' # bg white (49 is default)
+ @set_attribute_bits 0xff00, 0xff
+
+ else
+ # if we don't recognize the style, go back to default
+ @set_attribute_bits 0xffffff, 0
+ return
# str is the whole escape sequence (minus the esc[ prefix)
update_sequence: (str) ->
- for c in @sequence_callbacks
- c str
- console.log "sequence: \"#{str}\"" # FIXME
+ command = @["csi_#{str.substr str.length - 1}"]
+ return unless command?
+ args = str.substr(0, str.length - 1).split ';'
+ for i in [0...args.length]
+ if args[i] is ''
+ args[i] = command.default
+ command.go.call this, args...
update_sequence_then_text: (str) ->
len = @escape_sequence_length str