From 1700d1f6d76490cd696f9072bf28d595a4e484c5 Mon Sep 17 00:00:00 2001 From: Jason Woofenden Date: Wed, 30 Jan 2013 05:01:01 -0500 Subject: [PATCH] plain text flows and wraps properly --- index.html | 16 ++--- publish-my-session.coffee | 45 -------------- run.sh | 5 ++ server.coffee | 47 +++++++++++++++ terminal.coffee | 147 ++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 188 insertions(+), 72 deletions(-) delete mode 100644 publish-my-session.coffee create mode 100755 run.sh create mode 100644 server.coffee diff --git a/index.html b/index.html index d540026..091da51 100644 --- a/index.html +++ b/index.html @@ -7,18 +7,12 @@ diff --git a/publish-my-session.coffee b/publish-my-session.coffee deleted file mode 100644 index fec3467..0000000 --- a/publish-my-session.coffee +++ /dev/null @@ -1,45 +0,0 @@ -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() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..8c9ce4b --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# run from source directory + +script -f >(coffee server.coffee >log.txt 2>&1) diff --git a/server.coffee b/server.coffee new file mode 100644 index 0000000..3ff1529 --- /dev/null +++ b/server.coffee @@ -0,0 +1,47 @@ +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() diff --git a/terminal.coffee b/terminal.coffee index 94be906..360fb1a 100644 --- a/terminal.coffee +++ b/terminal.coffee @@ -6,25 +6,26 @@ class Terminal 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) -> @@ -43,19 +44,133 @@ class Terminal 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 -- 1.7.10.4