\ tag: terminal emulation
\ 
\ this code implements IEEE 1275-1994 ANNEX B
\ 
\ Copyright (C) 2003 Stefan Reinauer
\ 
\ See the file "COPYING" for further information about
\ the copyright and warranty status of this work.
\ 

0 value (escseq)
10 buffer: (sequence)

: (match-number) ( x y [1|2] [1|2] -- x [z] )
  2dup = if \ 1 1 | 2 2
    drop exit
  then
  2dup > if
    2drop drop 1 exit
  then
  2drop 0
  ;

: (esc-number) ( maxchar -- ?? ?? num )
  >r depth >r  ( R: depth maxchar )
  0 (sequence) 2+ (escseq) 2- ( 0 seq+2 seqlen-2 )
  \ if numerical, scan until non-numerical
  0 ?do
    ( 0 seq+2 )
    dup i + c@ a
    digit if
      ( 0 ptr  n )
      rot a * + ( ptr val )
      swap 
    else
      ( 0 ptr asc )
      ascii ; = if
        0 swap
      else
        drop leave
      then
    then
    
  loop
  depth r> - r>
  0 to (escseq)
  (match-number) 
  ;
  
: (match-seq)
  (escseq) 1- (sequence) + c@  \ get last character in sequence
  \ dup draw-character
  case
    ascii A of \ CUU - cursor up
      1 (esc-number) 
      0> if 
        1 max
      else 
        1
      then
      negate line# + 
      0 max to line#
    endof
    ascii B of \ CUD - cursor down
      1 (esc-number) 
      0> if 
        1 max
	line# + 
        #lines 1- min to line#
      then
    endof
    ascii C of \ CUF - cursor forward
      1 (esc-number) 
      0> if 
        1 max
	column# + 
        #columns 1- min to column#
      then
    endof
    ascii D of \ CUB - cursor backward
      1 (esc-number) 
      0> if 
        1 max
	negate column# + 
	0 max to column#
      then
    endof
    ascii E of \ Cursor next line (CNL) 
      \ FIXME - check agains ANSI3.64
      1 (esc-number) 
      0> if 
        1 max
	line# + 
        #lines 1- min to line#
      then
      0 to column#
    endof
    ascii f of
      2 (esc-number) 
      case
        2 of
          1- #columns 1- min to column#
          1- #lines 1- min to line#
        endof
        1 of
          0 to column#
          1- #lines 1- min to line#
        endof
        0 of
          0 to column#
          0 to line#
          drop
        endof
      endcase
    endof
    ascii H of
      2 (esc-number)
      case
        2 of
          1- #columns 1- min to column#
          1- #lines 1- min to line#
        endof
        1 of
          0 to column#
          1- #lines 1- min to line#
        endof
        0 of
          0 to column#
          0 to line#
          drop
        endof
      endcase
    endof
    ascii J of
      0 to (escseq)
      #columns column# - delete-characters
      #lines line# - delete-lines
    endof
    ascii K of
      0 to (escseq)
      #columns column# - delete-characters
    endof
    ascii L of
      1 (esc-number) 
      0> if
        1 max
        insert-lines
      then
    endof
    ascii M of
      1 (esc-number) 
      1 = if
        1 max
        delete-lines
      then
    endof
    ascii @ of
      1 (esc-number) 
      1 = if
        1 max
        insert-characters 
      then
    endof
    ascii P of
      1 (esc-number) 
      1 = if
	1 max
        delete-characters
      then
    endof
    ascii m of
      1 (esc-number)
      1 = if
        7 = if 
          true to inverse?
        else
          false to inverse?
        then
      then
    endof
    ascii p of \ normal text colors
      0 to (escseq)
      inverse-screen? if
        false to inverse-screen?
	inverse? 0= to inverse?
	invert-screen
      then
    endof
    ascii q of \ inverse text colors
      0 to (escseq)
      inverse-screen? not if
        true to inverse-screen?
	inverse? 0= to inverse?
	invert-screen
      then
    endof
    ascii s of
      \ Resets the display device associated with the terminal emulator.
      0 to (escseq)
      reset-screen
    endof
  endcase
  ;

: (term-emit) ( char -- )
  toggle-cursor
  
  (escseq) 0> if
    (escseq) 10 = if
      0 to (escseq)
      ." overflow in esc" cr
      drop
    then
    (escseq) 1 = if 
      dup ascii [ = if    \ not a [
        (sequence) 1+ c!
	2 to (escseq)
      else
        0 to (escseq)      \ break out of ESC sequence
	." out of ESC" cr
	drop               \ don't print breakout character
      then
      toggle-cursor exit
    else
      (sequence) (escseq) + c! 
      (escseq) 1+ to (escseq)
      (match-seq)
      toggle-cursor exit
    then  
  then
  
  case
  0 of \ NULL
    toggle-cursor exit
  endof
  7 of \ BEL
    blink-screen
    s" /screen" s" ring-bell" 
    execute-device-method
  endof
  8 of \ BS
    column# 0<> if
      column# 1- to column#
      toggle-cursor exit
    then
  endof
  9 of \ TAB
    column# dup #columns = if 
      drop
    else
      8 + -8 and ff and to column#
    then
    toggle-cursor exit
  endof
  a of \ LF
    line# 1+ to line#
    0 to column#
    line# #lines >= if
      0 to line#
      1 delete-lines
      #lines 1- to line#
      toggle-cursor exit
    then
  endof
  b of \ VT
    line# 0<> if
      line# 1- to line#
    then
    toggle-cursor exit
  endof
  c of \ FF
    0 to column# 0 to line#
    erase-screen
  endof
  d of \ CR
    0 to column#
    toggle-cursor exit
  endof
  1b of \ ESC
    1b (sequence) c!
    1 to (escseq)
  endof

  \ draw character and advance position
  column# #columns >= if
    0 to column#
    line# 1+ to line#
    line# #lines >= if
      0 to line#
      1 delete-lines
      #lines 1- to line#
    then
  then

  dup draw-character
  column# 1+ to column#

  endcase
  toggle-cursor
  ;

['] (term-emit) to fb-emit