JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
htmlterm: redraw on init
[watch-my-terminal.git] / server.coffee
1 # SETTINGS
2 term_lines = 34
3 term_columns = 104
4 http_port = 2218
5
6
7 handler = (req, res) ->
8         reply_err = (req, res, msg) ->
9                 res.writeHead(200, 'Content-Type': 'text/plain')
10                 return res.end("Error loading #{req.url} \"#{msg}\"")
11         switch req.url
12                 when '/', '/index.html'
13                         filename = __dirname + '/index.html'
14                         type = 'text/html'
15                 when '/jquery.js'
16                         filename = '/usr/share/javascript/jquery/jquery.min.js'
17                         type = 'text/javascript'
18                 when '/terminal.js'
19                         filename = __dirname + '/terminal.coffee'
20                         type = 'text/javascript'
21                 when '/htmlterm.js'
22                         filename = __dirname + '/htmlterm.coffee'
23                         type = 'text/javascript'
24                 when '/client.js'
25                         filename = __dirname + '/client.coffee'
26                         type = 'text/javascript'
27                 else
28                         return reply_err req, res
29         fs.readFile filename, 'utf8', (err, data) ->
30                 return reply_err req, res, err if err
31
32                 if filename.substr(filename.length - 7) is '.coffee'
33                         try
34                                 data = coffee.compile data
35                         catch e
36                                 return reply_err req, res, "server faild to compile #{filename}: #{JSON.stringify(e)}"
37
38                 res.writeHead(200, 'Content-Type': type)
39                 res.end(data)
40
41 coffee = require 'coffee-script'
42 app = require('http').createServer(handler)
43 io = require('socket.io').listen(app)
44 fs = require('fs')
45 terminal = require('./terminal.coffee')
46
47 app.listen(http_port)
48 term = terminal.new(term_columns, term_lines)
49
50 sockets = []
51
52 io.sockets.on 'connection', (socket) ->
53         sockets.push socket
54         socket.on 'disconnect', ->
55                 for i in [i...sockets.length]
56                         if sockets[i] is socket
57                                 sockets.splice i, 1
58                                 return
59
60
61         enc_color = (prefix, c) ->
62                 if c < 8
63                         return "#{prefix}#{c}"
64                 if c < 16
65                         return "#{prefix + 6}#{c - 8}"
66                 return "#{prefix}8;5;#{c}"
67
68         attr_diff = (a, b) ->
69                 xo = a ^ b
70                 parts = []
71                 if (xo & 0xff)
72                         parts.push enc_color 3, (b & 0xff)
73                 if (xo & 0xff00)
74                         parts.push enc_color 4, ((b & 0xff00) >> 8)
75                 for [bit, code] in [[0x010000, '1'], [0x200000, '3'], [0x020000, '4'], [0x040000, '5'], [0x080000, '7'], [0x100000, '8']]
76                         if (xo & bit)
77                                 if (b & bit)
78                                         parts.push code
79                                 else
80                                         parts.push '2' + code
81                 if parts.length
82                         return "\x1b[#{parts.join ';'}m"
83                 else
84                         return ''
85         encode_screen = (height, width, text, attributes) ->
86                 state = ''
87                 for y in [0...height]
88                         max = width - 1
89                         while max >= 0 and text[y][max] is ' ' and (attributes[y][max] & 0x08ff00) is 0
90                                 max -= 1
91                         if max >= 0
92                                 for x in [0..max]
93                                         if attributes[y][x] isnt a
94                                                 state += attr_diff a, attributes[y][x]
95                                                 a = attributes[y][x]
96                                         state += text[y][x]
97                         if y < height - 1
98                                 state += '\n'
99                 return state
100
101         a = 0x07
102         state = ''
103         if term.saved_normal_screen?
104                 state += encode_screen term.height, term.width, term.saved_normal_screen.text, term.saved_normal_screen.attributes
105                 state += "\x1b[#{term.saved_normal_screen.y + 1};#{term.saved_normal_screen.x + 1}H"
106                 state += "\x1b[?1049h\x1b[H" # flip to alt screen and move cursor home
107         state += encode_screen term.height, term.width, term.text, term.attributes
108         state += attr_diff a, term.a
109         state += "\x1b[#{term.y + 1};#{term.x + 1}H"
110         unless term.cursor_visible
111                 state += "\x1b[?25l"
112         unless term.scroll_top is 0 and term.scroll_bottom is term.height - 1
113                 state += "\x1b[#{term.scroll_top};#{term.scroll_bottom}r"
114         socket.emit 'init', width: term.width, height: term.height, text: state
115
116 process.stdin.resume()
117 process.stdin.setEncoding 'utf8'
118
119 process.stdin.on 'data', (data) ->
120         term.update data
121         for s in sockets
122                 s.emit 'data', data
123
124 process.stdin.on 'end', -> process.exit()