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
256 arg = @fix_esc_arg i, ''
259 @cursor_visible = true
261 if @saved_normal_screen?
262 console.log "ignoring request to switch to the alt screen because we're already on the alt screen"
264 @saved_normal_screen = [@x, @y, @text, @attributes]
267 for y in [0...@height]
270 for x in [0...@width]
272 @attributes[y].push 0x07
274 console.log "confusing arg for csiq_h: #{arg}"
281 arg = @fix_esc_arg i, ''
284 @cursor_visible = false
286 if not @saved_normal_screen?
287 console.log "ignoring request to switch to the normal screen because we're already on the normal screen"
289 @x = @saved_normal_screen[0]
290 @y = @saved_normal_screen[1]
291 @text = @saved_normal_screen[2]
292 @attributes = @saved_normal_screen[3]
293 @saved_normal_screen = null
295 console.log "confusing arg for csiq_l: #{arg}"
298 # set color, bold, underline, etc
302 args.push @fix_esc_arg i, '0'
304 while args.length > 0
307 # remove all style/color
313 @set_attribute_bits 0x010000, 0x010000
314 when '3' # italic (rare)
315 @set_attribute_bits 0x200000, 0x200000
317 @set_attribute_bits 0x020000, 0x020000
319 @set_attribute_bits 0x040000, 0x040000
321 @set_attribute_bits 0x080000, 0x080000
322 when '8' # invisible. urivt ignores this
323 @set_attribute_bits 0x100000, 0x100000
325 # disable style attributes
326 when '21' # not bold (rare)
327 @set_attribute_bits 0x010000, 0
329 @set_attribute_bits 0x010000, 0
330 when '23' # not italic (rare)
331 @set_attribute_bits 0x200000, 0
332 when '24' # not underline
333 @set_attribute_bits 0x020000, 0
334 when '25' # not blink
335 @set_attribute_bits 0x040000, 0
336 when '27' # not inverse
337 @set_attribute_bits 0x080000, 0
338 when '28' # not invisible
339 @set_attribute_bits 0x100000, 0
341 when '100' # reset colors but not other attributes
342 @set_attribute_bits 0xffff, 0x0007
346 @set_attribute_bits 0xff, 0x00
348 @set_attribute_bits 0xff, 0x01
350 @set_attribute_bits 0xff, 0x02
351 when '33' # fg yellow
352 @set_attribute_bits 0xff, 0x03
354 @set_attribute_bits 0xff, 0x04
355 when '35' # fg magenta
356 @set_attribute_bits 0xff, 0x05
358 @set_attribute_bits 0xff, 0x06
359 when '37', '39' # fg white (39 is default)
360 @set_attribute_bits 0xff, 0x07
363 if args.length >= 2 and args[0] is '5'
365 @set_attribute_bits 0xff, (0xff & args.shift())
367 @set_attribute_bits 0x20000, 0x20000
371 @set_attribute_bits 0xff00, 0x0000
373 @set_attribute_bits 0xff00, 0x0100
375 @set_attribute_bits 0xff00, 0x0200
376 when '43' # bg yellow
377 @set_attribute_bits 0xff00, 0x0300
379 @set_attribute_bits 0xff00, 0x0400
380 when '45' # bg magenta
381 @set_attribute_bits 0xff00, 0x0500
383 @set_attribute_bits 0xff00, 0x0600
385 @set_attribute_bits 0xff00, 0x0700
386 when '49' # bg default
387 @set_attribute_bits 0xff00, 0x0000
390 if args.length >= 2 and args[0] is '5'
392 @set_attribute_bits 0xff00, ((0xff & args.shift()) << 8)
394 @set_attribute_bits 0x20000, 0x20000
397 when '90' # fg bright black
398 @set_attribute_bits 0xff, 0x08
399 when '91' # fg bright red
400 @set_attribute_bits 0xff, 0x09
401 when '92' # fg bright green
402 @set_attribute_bits 0xff, 0x0a
403 when '93' # fg bright yellow
404 @set_attribute_bits 0xff, 0x0b
405 when '94' # fg bright blue
406 @set_attribute_bits 0xff, 0x0c
407 when '95' # fg bright magenta
408 @set_attribute_bits 0xff, 0x0d
409 when '96' # fg bright cyan
410 @set_attribute_bits 0xff, 0x0e
411 when '97' # fg bright white
412 @set_attribute_bits 0xff, 0x0f
415 when '100' # bg bright black
416 @set_attribute_bits 0xff, 0x08
417 when '101' # bg bright red
418 @set_attribute_bits 0xff, 0x09
419 when '102' # bg bright green
420 @set_attribute_bits 0xff, 0x0a
421 when '103' # bg bright yellow
422 @set_attribute_bits 0xff, 0x0b
423 when '104' # bg bright blue
424 @set_attribute_bits 0xff, 0x0c
425 when '105' # bg bright magenta
426 @set_attribute_bits 0xff, 0x0d
427 when '106' # bg bright cyan
428 @set_attribute_bits 0xff, 0x0e
429 when '107' # bg bright white
430 @set_attribute_bits 0xff, 0x0f
433 # if we don't recognize the style, go back to default
434 console.log "unrecognized csi_m arg: \"#{arg}\""
438 # set scrolling region
439 csi_r: (top, bottom) ->
440 top = -1 + parseInt @fix_esc_arg top, '1'
441 bottom = -1 + parseInt @fix_esc_arg bottom, '10000'
447 @scroll_bottom = bottom
450 # str is the whole escape sequence (minus the esc[ prefix)
451 update_sequence: (str) ->
456 command = @[prefix + str.substr(str.length - 1)]
458 console.log "Unrecognized sequence: ESC[#{str}"
460 args = str.substr(0, str.length - 1).split ';'
461 command.call this, args...
463 update_sequence_then_text: (str) ->
464 len = @escape_sequence_length str
466 console.log "couldn't find escape sequence here: #{str.substr 0, 25}"
467 @update_text "ESC[" + str
469 @update_sequence str.substr 0, len
470 @update_text str.substr len
472 escape_sequence_length: (str) ->
473 parts = str.match(/^[0-9;?]{0,25}./)
474 return -1 unless parts?
475 return parts[0].length
477 my_exports.new = (width, height) ->
478 return new Terminal width, height