1 # this file is used by the client and server.
3 # work around lack of module system in the browser:
8 my_exports = window.terminal
12 constructor: (width, height) ->
19 @a = 0x000007 # cursor attributes
21 @saved_normal_screen = null
22 @cursor_visible = true
24 @scroll_bottom = height - 1
27 resize: (width, height) ->
28 # FIXME: write a version that retains some of the data
29 # FIXME: clamp variables (eg x, y, saved.*, scrolling region) if getting smaller
39 @attributes[y].push 0x07
41 # pass data from stdout
43 return unless data?.length > 0
44 if @partial.length > 0
45 data = @partial + data
47 parts = data.split(/\x1b\[/)
49 if -1 is @escape_sequence_length parts[parts.length - 1]
50 @partial = parts.pop()
52 for i in [0...parts.length]
56 @update_sequence_then_text parts[i]
60 # clear the line at the top of the scrolling region
62 @text[@scroll_top][i] = ' '
63 @attributes[@scroll_top][i] = 0x07
67 a[0...@scroll_top]..., # up to but not including scroll top
68 a[@scroll_top + 1 .. @scroll_bottom]..., # scroll region except top line of it
69 a[@scroll_top], # top line of scroll region (already cleared)
70 a[@scroll_bottom + 1 ... @height]... # rest of screen
72 @text = rearrange @text
73 @attributes = rearrange @attributes
75 # slide cursor up with rest of text
79 if @y is @scroll_bottom
84 # str has no escape sequences
86 return unless str.length > 0
90 @update_text " ".substr(@x % 8)
95 when '\x08' # backspace
102 when '\x0a', '\x0b' # lf, vertical tab (same thing)
108 @attributes[@y][@x] = @a
112 set_attribute_bits: (mask, value) ->
113 @a = ((@a & ~mask) | value)
115 # we're supposed to ignore leeding zeros, and while we're at it, lets swap
116 # in the default for blank or missing values
117 fix_esc_arg: (value, deef_alt) ->
118 if value? and value != ''
119 while value[0] is '0' and value.length > 1
120 value = value.substr 1
125 # csi_@: rxvt does nothing I can detect
129 lines = parseInt @fix_esc_arg lines, '1'
137 lines = parseInt @fix_esc_arg lines, '1'
145 cols = parseInt @fix_esc_arg cols, '1'
153 cols = parseInt @fix_esc_arg cols, '1'
159 # set cursor position (one based)
160 csi_H: (row, column) ->
161 # handle blank/missing args and convert to 0 base
162 row = -1 + parseInt @fix_esc_arg row, '1'
163 column = -1 + parseInt @fix_esc_arg column, '1'
168 else if column >= @width
180 # clear to screen edge(es)
181 csi_J: (direction) ->
182 switch @fix_esc_arg direction, '0'
183 when '0' # erase down
184 # rest of current line
187 for row in [@y...@height]
188 for i in [0...@width]
190 @attributes[row][i] = @a
192 # beginning of current line
196 for i in [0...@width]
198 @attributes[row][i] = @a
199 when '2' # erase everything
200 for row in [0...@height]
201 for i in [0...@width]
203 @attributes[row][i] = @a
205 console.log "confusing arg for csi_J: #{direction}"
208 # clear (some or all of) current line
209 csi_K: (direction) ->
210 switch @fix_esc_arg direction, '0'
211 when '0' # erase to right
212 for i in [@x...@width]
214 @attributes[@y][i] = @a
215 when '1' # erase to left
216 # @x can equal @width (after printing to right-most column)
223 @attributes[@y][i] = @a
224 when '2' # erase whole line
225 for i in [0...@width]
227 @attributes[@y][i] = @a
229 console.log "confusing arg for csi_K: #{direction}"
232 # move lines downwards (arg is how far)
234 lines = parseInt @fix_esc_arg lines, '1'
238 a[0...@y]..., # keep everything above cursor
239 a[@scroll_bottom - lines + 1 .. @scroll_bottom]..., # we'll clear these shortly
240 a[@y..@scroll_bottom - lines]..., # lines that are moving down
241 a[@scroll_bottom + 1 ... @height]... # rest of screen
243 @text = rearrange @text
244 @attributes = rearrange @attributes
246 # clear the lines we scrolled off (and put back in as "new")
247 for y in [@y...@y+lines]
248 for x in [0...@width]
250 @attributes[y][x] = 0x07
252 # move lines upwards (arg is how far)
253 # this obliterates the line under the cursor and arg-1 following it
255 lines = parseInt @fix_esc_arg lines, '1'
259 a[0 ... @y]..., # keep everything above cursor
260 a[@y + lines .. @scroll_bottom]..., # lines we're moving up
261 a[@y ... @y + lines]..., # recycle these
262 a[@scroll_bottom + 1 ... @height]... # keep the rest
264 @text = rearrange @text
265 @attributes = rearrange @attributes
267 # clear the lines we're recycling
268 for y in [@scroll_bottom - lines + 1 .. @scroll_bottom]
269 for x in [0...@width]
271 @attributes[y][x] = 0x07
277 arg = @fix_esc_arg i, ''
280 @cursor_visible = true
282 if @saved_normal_screen?
283 console.log "ignoring request to switch to the alt screen because we're already on the alt screen"
285 @saved_normal_screen = [@x, @y, @text, @attributes]
288 for y in [0...@height]
291 for x in [0...@width]
293 @attributes[y].push 0x07
295 console.log "confusing arg for csiq_h: #{arg}"
302 arg = @fix_esc_arg i, ''
305 @cursor_visible = false
307 if not @saved_normal_screen?
308 console.log "ignoring request to switch to the normal screen because we're already on the normal screen"
310 @x = @saved_normal_screen[0]
311 @y = @saved_normal_screen[1]
312 @text = @saved_normal_screen[2]
313 @attributes = @saved_normal_screen[3]
314 @saved_normal_screen = null
316 console.log "confusing arg for csiq_l: #{arg}"
319 # set color, bold, underline, etc
323 args.push @fix_esc_arg i, '0'
325 while args.length > 0
328 # remove all style/color
334 @set_attribute_bits 0x010000, 0x010000
335 when '3' # italic (rare)
336 @set_attribute_bits 0x200000, 0x200000
338 @set_attribute_bits 0x020000, 0x020000
340 @set_attribute_bits 0x040000, 0x040000
342 @set_attribute_bits 0x080000, 0x080000
343 when '8' # invisible. urivt ignores this
344 @set_attribute_bits 0x100000, 0x100000
346 # disable style attributes
347 when '21' # not bold (rare)
348 @set_attribute_bits 0x010000, 0
350 @set_attribute_bits 0x010000, 0
351 when '23' # not italic (rare)
352 @set_attribute_bits 0x200000, 0
353 when '24' # not underline
354 @set_attribute_bits 0x020000, 0
355 when '25' # not blink
356 @set_attribute_bits 0x040000, 0
357 when '27' # not inverse
358 @set_attribute_bits 0x080000, 0
359 when '28' # not invisible
360 @set_attribute_bits 0x100000, 0
362 when '100' # reset colors but not other attributes
363 @set_attribute_bits 0xffff, 0x0007
367 @set_attribute_bits 0xff, 0x00
369 @set_attribute_bits 0xff, 0x01
371 @set_attribute_bits 0xff, 0x02
372 when '33' # fg yellow
373 @set_attribute_bits 0xff, 0x03
375 @set_attribute_bits 0xff, 0x04
376 when '35' # fg magenta
377 @set_attribute_bits 0xff, 0x05
379 @set_attribute_bits 0xff, 0x06
380 when '37', '39' # fg white (39 is default)
381 @set_attribute_bits 0xff, 0x07
384 if args.length >= 2 and args[0] is '5'
386 @set_attribute_bits 0xff, (0xff & args.shift())
388 @set_attribute_bits 0x20000, 0x20000
392 @set_attribute_bits 0xff00, 0x0000
394 @set_attribute_bits 0xff00, 0x0100
396 @set_attribute_bits 0xff00, 0x0200
397 when '43' # bg yellow
398 @set_attribute_bits 0xff00, 0x0300
400 @set_attribute_bits 0xff00, 0x0400
401 when '45' # bg magenta
402 @set_attribute_bits 0xff00, 0x0500
404 @set_attribute_bits 0xff00, 0x0600
406 @set_attribute_bits 0xff00, 0x0700
407 when '49' # bg default
408 @set_attribute_bits 0xff00, 0x0000
411 if args.length >= 2 and args[0] is '5'
413 @set_attribute_bits 0xff00, ((0xff & args.shift()) << 8)
415 @set_attribute_bits 0x20000, 0x20000
418 when '90' # fg bright black
419 @set_attribute_bits 0xff, 0x08
420 when '91' # fg bright red
421 @set_attribute_bits 0xff, 0x09
422 when '92' # fg bright green
423 @set_attribute_bits 0xff, 0x0a
424 when '93' # fg bright yellow
425 @set_attribute_bits 0xff, 0x0b
426 when '94' # fg bright blue
427 @set_attribute_bits 0xff, 0x0c
428 when '95' # fg bright magenta
429 @set_attribute_bits 0xff, 0x0d
430 when '96' # fg bright cyan
431 @set_attribute_bits 0xff, 0x0e
432 when '97' # fg bright white
433 @set_attribute_bits 0xff, 0x0f
436 when '100' # bg bright black
437 @set_attribute_bits 0xff, 0x08
438 when '101' # bg bright red
439 @set_attribute_bits 0xff, 0x09
440 when '102' # bg bright green
441 @set_attribute_bits 0xff, 0x0a
442 when '103' # bg bright yellow
443 @set_attribute_bits 0xff, 0x0b
444 when '104' # bg bright blue
445 @set_attribute_bits 0xff, 0x0c
446 when '105' # bg bright magenta
447 @set_attribute_bits 0xff, 0x0d
448 when '106' # bg bright cyan
449 @set_attribute_bits 0xff, 0x0e
450 when '107' # bg bright white
451 @set_attribute_bits 0xff, 0x0f
454 # if we don't recognize the style, go back to default
455 console.log "unrecognized csi_m arg: \"#{arg}\""
459 # set scrolling region
460 csi_r: (top, bottom) ->
461 top = -1 + parseInt @fix_esc_arg top, '1'
462 bottom = -1 + parseInt @fix_esc_arg bottom, '10000'
468 @scroll_bottom = bottom
471 # str is the whole escape sequence (minus the esc[ prefix)
472 update_sequence: (str) ->
477 command = @[prefix + str.substr(str.length - 1)]
479 console.log "Unrecognized sequence: ESC[#{str}"
481 args = str.substr(0, str.length - 1).split ';'
482 command.call this, args...
484 update_sequence_then_text: (str) ->
485 len = @escape_sequence_length str
487 console.log "couldn't find escape sequence here: #{str.substr 0, 25}"
488 @update_text "ESC[" + str
490 @update_sequence str.substr 0, len
491 @update_text str.substr len
493 escape_sequence_length: (str) ->
494 parts = str.match(/^[0-9;?]{0,25}./)
495 return -1 unless parts?
496 return parts[0].length
498 my_exports.new = (width, height) ->
499 return new Terminal width, height