/*{{{}}}*/
/*{{{  includes*/
#ifdef CONFIG_H
#   include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>

#define I_DISPLAY_C
#define I_GETTK_C
#define I_MAIN_C
#define I_SCREEN_C
#define I_SIGNALS_C
#define SCRBUFF_C

#include "origami.h"
#include <lib/ori_add_lib.h>
#include <h/envvar_str.h>
/*}}}  */

/*{{{  types*/
/*{{{  pentype*/
#define PEN_MASK (~(O_NOP-1))
typedef enum { NORM=0,HIGHLIGHT=O_NOP,SG_SPACE=PEN_MASK } pentype;
/*}}}  */
/*{{{  c_p_type - coding pen and character*/
#define c_p_code_gen(c,p) ((c)|(p))

typedef enum
 { SPACE_CODE=c_p_code_gen(' ',NORM),
   SO_CODE   =c_p_code_gen('@',SG_SPACE),
   SE_CODE   =c_p_code_gen('$',SG_SPACE)
 } c_p_code;

#define B_code_c_p(c,p) ((c_p_code)c_p_code_gen(c,p))
#define B_get_pen(c)    ((c)&PEN_MASK)
#define B_get_char(c)   ((c)&(~(PEN_MASK)))
#define B_test_pen(c,p) (B_get_pen(c)==(p))
/*}}}  */
/*{{{  chg_type - how is a line changed*/
typedef enum { OK=0,CHANGED,INVALID } chg_type;
/*}}}  */

typedef struct
 { c_p_code *code;  /* char and pen data */
   chg_type mode; /* changed? */
   int id;          /* line number on old screen */
 } linebuffer;
/*}}}  */
/*{{{  variables*/
private linebuffer *B_scr_data=0;   /* malloced space for 2 buffers */

private boolean B_buf_id;           /* tag for current of 2 buffers */
private linebuffer *B_current_scr=0;/* Pointer to current screen buffer */

private c_p_code *B_empty_line;     /* screen.w sized array with SPACE_CODE */
private c_p_code *B_buff_line;      /* screen.w sized array */

private linebuffer *B_cur_line;     /* pointer to current line in buffer */

private win_data B_pos={0,0};       /* buffer cursor */
private pentype B_pen;              /* current pen */

private boolean B_clr_scr_used=True;/* should ClrScr used first on flush */
private int B_new_line;             /* no of deleted/scrolled out lines */
private boolean B_use_dl_al;        /* should next flush use il/al */
private chg_type B_scr_mode;        /* do a full redraw at next flush */
private boolean B_check_so_glitch;  /* any standout glitch to be handled */

private boolean B_do_buffer=False;  /* is terminal initialised */
private boolean B_active=False;     /* buffered termout active */
/*}}}  */

/*{{{  screen handling*/
private win_data scr_pos={ 0,0 };
/*{{{  S_invalid_pos*/
#define S_invalid_pos() (scr_pos.h=0)
/*}}}  */
/*{{{  S_shift_pos*/
#define S_shift_pos(x) \
  do { if ((scr_pos.w+=x)>screen.w) S_invalid_pos(); } while (NEVER)
/*}}}  */
/*{{{  S_step_or_move*/
private void S_step_or_move(int y,int x)
{
  if (scr_pos.h==0)
     move_cursor_to(y,x);
  else if (scr_pos.h!=y || scr_pos.w!=x)
     step_or_move_cursor(y,x,y-scr_pos.h,x-scr_pos.w);
  else
     return;
  scr_pos.h=y;
  scr_pos.w=x;
}
/*}}}  */
/*{{{  S_moveclreol*/
#define S_moveclreol(y,x) (moveclreol((y),(x)),S_invalid_pos())
/*}}}  */
/*{{{  S_ClrScr*/
#define S_ClrScr() (ClrScr(),S_invalid_pos())
/*}}}  */
/*}}}  */
/*{{{  internal buffer handling procedures*/
/*{{{  B_check_active*/
#define B_check_active() (redraw_on_sig=!(B_active=B_do_buffer&&B_scr_data))
/*}}}  */

/*{{{  B_get_start*/
#define B_get_start(i) (B_current_scr=B_scr_data-1+((i)?screen.h:0))
/*}}}  */
/*{{{  B_toggle_start*/
#define B_toggle_start() B_get_start(B_buf_id= !B_buf_id)
/*}}}  */

/*{{{  B_check_h_position*/
#define B_check_h_pos(ph) \
   do{if((ph)<=0)(ph)=1;else if((ph)>screen.h)(ph)=screen.h;}while(NEVER)
/*}}}  */
/*{{{  B_check_high_w_position*/
#define B_check_high_w_pos(pw) \
   do{if((pw)>screen.w)(pw)=screen.w;}while(NEVER)
/*}}}  */
/*{{{  B_check_w_position*/
#define B_check_w_pos(pw) \
   do{if((pw)<=0)(pw)=1;else B_check_high_w_pos(pw);}while(NEVER)
/*}}}  */
/*{{{  B_check_position*/
#define B_check_pos(ph,pw) \
   do{B_check_h_pos(ph);B_check_w_pos(pw);}while(NEVER)
/*}}}  */
/*{{{  B_inc_w_position*/
#define B_inc_w_position(p) do{if(++(p)>screen.w)(p)=screen.w;}while(NEVER)
/*}}}  */
/*{{{  B_update_line_pointer*/
#define B_update_line_pointer() (B_cur_line=B_current_scr+B_pos.h)
/*}}}  */

/*{{{  B_check_cursor*/
private void B_check_cursor(void)
{
  B_check_pos(B_pos.h,B_pos.w);
  B_update_line_pointer();
}
/*}}}  */

/*{{{  B_copy*/
#define B_copy(d,s,l) memcpy((d),(s),(l)*sizeof(c_p_code))
/*}}}  */
/*{{{  B_cmp*/
#define B_cmp(s1,s2,l) memcmp((s1),(s2),(l)*sizeof(c_p_code))
/*}}}  */
/*{{{  B_clr*/
#define B_clr(l,f,t) B_copy((l)+(f),B_empty_line,(t)-(f)+1)
/*}}}  */

/*{{{  B_al_dl_rotate*/
private void B_al_dl_rotate(int from,int to)
{
  linebuffer c;
  int diff;

  c=B_current_scr[from];
  for (diff=(from<=to)?1:-1;from!=to;from+=diff)
     B_current_scr[from]=B_current_scr[from+diff];
  if (c.id)
   { B_new_line++;
     c.id=0;
   }
  c.mode=CHANGED;
  B_current_scr[to]=c;
}
/*}}}  */
/*}}}  */

/*{{{  B_oputc*/
public void B_oputc(int c)
{
  if (!scr_off)
     if (B_active)
      { if (c=='~' && hz)
         { B_oputc(CTRL_MARK);
           c=HZ_TILDE;
         }
        B_cur_line->code[B_pos.w]=B_code_c_p(c,B_pen);
        B_inc_w_position(B_pos.w);
        B_cur_line->mode=CHANGED;
      }
     else
        oputc(c);
}
/*}}}  */
/*{{{  B_oputs*/
public void B_oputs(unsigned char*s)
{
  if (!scr_off)
     if (B_active)
      { B_cur_line->mode=CHANGED;
        for (;;s++)
         { unsigned char c;
           switch ((c= *s))
            { case '~':
                 if (hz)
                  { B_cur_line->code[B_pos.w]=B_code_c_p(CTRL_MARK,B_pen);
                    B_inc_w_position(B_pos.w);
                    c=HZ_TILDE;
                  }
              default:
                 B_cur_line->code[B_pos.w]=B_code_c_p(c,B_pen);
                 B_inc_w_position(B_pos.w);
                 continue;
              case '\0':
                 break;
            }
           break;

         }
      }
     else
        oputs(s);
}
/*}}}  */
/*{{{  B_move_cursor_to*/
public void B_move_cursor_to(int y,int x)
{
  if (!scr_off)
     if (B_active)
      { B_pos.h=y;
        B_pos.w=x;
        B_check_cursor();
      }
     else
        move_cursor_to(y,x);
}
/*}}}  */
/*{{{  B_moveclreol*/
public void B_moveclreol(int y,int x)
{
  if (!scr_off)
     if (B_active)
      { B_move_cursor_to(y,x);
        B_cur_line->mode=CHANGED;
        B_clr(B_cur_line->code,B_pos.w,screen.w);
      }
     else
        moveclreol(y,x);
}
/*}}}  */
/*{{{  B_ClrScr*/
public void B_ClrScr(void)
{
  B_clr_scr_used=True;
  if (!scr_off)
     if (B_active)
      { int l;

        for (l=screen.h;l;B_moveclreol(l--,1));
      }
     else
        ClrScr();
}
/*}}}  */
/*{{{  B_insLine*/
public boolean B_insLine(int n)
{
  if (B_active)
   { if (!scr_off)
        while (n-->0)
         { B_al_dl_rotate(screen.h,B_pos.h);
           B_clr(B_current_scr[B_pos.h].code,1,screen.w);
         }
     return(False);
   }
  else
     return(n!=1 || insLine(1));
}
/*}}}  */
/*{{{  B_DelLine*/
public boolean B_DelLine(int n)
{
  if (B_active)
   { if (!scr_off)
        while (n-->0)
         { B_al_dl_rotate(B_pos.h,screen.h);
           B_clr(B_current_scr[screen.h].code,1,screen.w);
         }
     return(False);
   }
  else
     return(n!=1 || DelLine(1));
}
/*}}}  */
/*{{{  B_scroll_region*/
/*{{{  B_rotate*/
private void B_rotate(int y1,int x1,int y2,int x2,boolean up)
{
  int l;

  /* no full check for valid edges! */
  if ((l=x2-x1+1))
   { int from,to,diff;

     /*{{{  prepare loop variables*/
     if (up)
      { from=y1;to=y2;diff=  1; }
     else
      { from=y2;to=y1;diff= -1; }
     /*}}}  */
     /*{{{  save scrolled out on buffer line*/
     B_copy(B_buff_line,B_current_scr[from].code+x1,l);
     /*}}}  */
     /*{{{  shift up/down*/
     for (;from!=to;from+=diff)
      { B_current_scr[from].mode=CHANGED;
        B_copy
         ( B_current_scr[from].code+x1,
           B_current_scr[from+diff].code+x1,
           l
         );
      }
     /*}}}  */
     /*{{{  put saved text back again*/
     B_current_scr[to].mode=CHANGED;
     B_copy(B_current_scr[from].code+x1,B_buff_line,l);
     /*}}}  */
   }
}
/*}}}  */

public boolean B_scroll_region(int y1,int x1,int y2,int x2,boolean up)
{
  if (!scr_off)
     if (B_active)
      /*{{{  scroll in buffer*/
      { B_check_pos(y1,x1);
        B_check_pos(y2,x2);
        /*{{{  maybe change y*/
        if (y1>y2)
         { int l;

           l=y1;
           y1=y2;
           y2=l;
         }
        /*}}}  */
        /*{{{  maybe change x*/
        if (x1>x2)
         { int l;

           l=x1;
           x1=x2;
           x2=l;
         }
        /*}}}  */
        if (y1!=y2)
         /*{{{  do the scrolling*/
         {
           /*{{{  rotate*/
           if
            (    SCROLL_INVERSE(x2-x1,screen.w)
#           ifdef BLOCK_FLUSH
              && (x1==1 || x2==screen.w)
#           endif
            )
            /*{{{  scroll using al/dl*/
            {
              /*{{{  rotate all lines in region*/
              if (up)
                 B_al_dl_rotate(y1,y2);
              else
                 B_al_dl_rotate(y2,y1);
              /*}}}  */
              /*{{{  maybe rotate remaining screen parts back in buffer!*/
              if (x1!=1)
                 B_rotate(y1,1,y2,x1-1,!up);
              if (x2!=screen.w)
                 B_rotate(y1,x2+1,y2,screen.w,!up);
              /*}}}  */
            }
            /*}}}  */
           else
            /*{{{  scroll in buffer*/
              B_rotate(y1,x1,y2,x2,up);
            /*}}}  */
           /*}}}  */
           /*{{{  clear part of moved line*/
           B_clr(B_current_scr[up?y2:y1].code,x1,x2);
           /*}}}  */
         }
         /*}}}  */
        return(False);
      }
      /*}}}  */
  return(True);
}
/*}}}  */

/*{{{  B_invalid*/
public void B_invalid(void)
{
  B_scr_mode=INVALID;
}
/*}}}  */
/*{{{  B_valid*/
public void B_valid(void)
{
  int y;

  B_scr_mode=OK;
  B_clr_scr_used=False;
  for (y=screen.h;y;B_current_scr[y--].mode=OK);
}
/*}}}  */
/*{{{  B_oflush*/
/*{{{  cursor_hide/push_position*/
private boolean cursor_hidden=False;
private win_data save_cursor_pos;
/*{{{  csr_push_pos*/
#define csr_push_pos() (save_cursor_pos=B_pos)
/*}}}  */
/*{{{  csr_pop_pos*/
#define csr_pop_pos()                                                   \
   while (scr_pos.w!=save_cursor_pos.w || scr_pos.h!=save_cursor_pos.h) \
    { B_pos=save_cursor_pos;                                            \
      redraw_on_sig=True;                                               \
      S_step_or_move(save_cursor_pos.h,save_cursor_pos.w);              \
      redraw_on_sig=False;                                              \
      break;                                                            \
    }
/*}}}  */
/*{{{  csr_hide*/
#define csr_hide()                                                      \
   while (!cursor_hidden)                                               \
    { cursor_hidden=True;                                               \
      redraw_on_sig=True;                                               \
      scr_cursor(False);                                                \
      redraw_on_sig=False;                                              \
      break;                                                            \
    }
/*}}}  */
/*{{{  csr_no_hide*/
#define csr_no_hide()                                                   \
   while (cursor_hidden)                                                \
    { redraw_on_sig=True;                                               \
      scr_cursor(True);                                                 \
      redraw_on_sig=False;                                              \
      break;                                                            \
    }
/*}}}  */
/*}}}  */
/*{{{  fl_upd_line*/
#ifndef fl_overwrite
  /*{{{  fl_toggle_pen*/
  private void fl_toggle_pen(pentype *p)
  {
    if (sg) S_invalid_pos();
    if (*p==NORM)
     { do_standout();
       *p=HIGHLIGHT;
     }
    else
     { do_standend();
       *p=NORM;
     }
  }
  /*}}}  */
#endif
#ifndef BLOCK_FLUSH
  /*{{{  fl_test_shift*/
  private int fl_test_shift
   ( const c_p_code *left,
     const c_p_code *right,
     const int to,
     const int max
   )
   { int l;
   
     for (left-=(l=1);l<=max;l++,left--)
        if (l)
         { int i;
         
           for (i=l;;i++)
              if (i>to)
                 return(l);
              else if (left[i]!=right[i])
                 break;
         }
     return(0);
   }
  /*}}}  */
  /*{{{  fl_m_insert*/
  private void fl_m_insert(const c_p_code *s, int lg)
   { int l;
     pentype p;
     unsigned char buff[FL_SHIFT_MAX];
   
     for (l=0,p=NORM;lg--;)
      /*{{{  move chars in buffer*/
      { c_p_code c;

        c= *s++;
        if (B_get_pen(c)!=p)
         /*{{{  change pen*/
         { if (l)
            /*{{{  print old*/
            { insChar(buff,l);
              S_shift_pos(l);
              l=0;
            }
            /*}}}  */
           fl_toggle_pen(&p);
         }
         /*}}}  */
        /*{{{  store char in buff*/
        buff[l++]=B_get_char(c);
        /*}}}  */
      }
      /*}}}  */
     if (l)
      /*{{{  print missing chars*/
      { insChar(buff,l);
        S_shift_pos(l);
      }
      /*}}}  */
     if (p!=NORM)
        fl_toggle_pen(&p);
   }
  /*}}}  */
#endif
#ifndef fl_overwrite       /* can be defined for special window systems ! */
  /*{{{  fl_overwrite*/
  /*{{{  buffered output for chars*/
#  ifdef BLOCK_FLUSH
     /*{{{  direct print, no buffering*/
#     define over_init()
#     define over_flush()
#     define over_put(c) oputc(c)
     /*}}}  */
#  else
     private int n_buff;
     private unsigned char c_buff;
     private boolean skip_right;
     /*{{{  over_init*/
#     define over_init() (c_buff=skip_right=n_buff=0)
     /*}}}  */
     /*{{{  over_flush*/
     private void over_fl_int(void)
     {
       if (skip_right)
        { step_or_move_cursor(scr_pos.h,scr_pos.w-n_buff,0,skip_right);
          S_shift_pos(n_buff-1);
          skip_right=0;
        }
       skip_right+=moputc(c_buff,n_buff);
       n_buff=0;
     }
#     define over_flush() (n_buff?over_fl_int():(void)0)
     /*}}}  */
     /*{{{  over_skip*/
#     define over_skip(w) (over_flush(),skip_right+=(w))
     /*}}}  */
     /*{{{  over_put*/
#     define over_put(ch) \
       (((ch)==c_buff)?n_buff++:(over_flush(),c_buff=(ch),n_buff=1))
     /*}}}  */
#  endif
  /*}}}  */

  private void fl_overwrite
   ( int y,
     int from_txt,
     int to_txt,
     int end_space,
     const c_p_code *cnew
#    ifndef BLOCK_FLUSH
        ,const c_p_code *cold
#    endif
   )
  {
    /*{{{  variables*/
    int x;
    int to_over;
    pentype fl_pen;
    /*}}}  */

    over_init();
    /*{{{  maybe disable skiping equal parts*/
#    ifndef BLOCK_FLUSH
      if (to_txt-from_txt<=FL_SHIFT_OVER)
         cold=0;
#    endif
    /*}}}  */
    /*{{{  maybe use clear_to_end_of_line*/
    if (end_space<=to_txt)
       to_over=end_space-1;
    else
     { to_over=to_txt;
       end_space=0;
     }
    /*}}}  */
    S_step_or_move(y,from_txt);
    for (x=from_txt,fl_pen=NORM;x<=to_over;x++)
     /*{{{  print the charater*/
     { int c;

       c=cnew[x];
       /*{{{  maybe switch to other pen (sg-chars also switch)*/
       if (B_get_pen(c)!=fl_pen)
        { over_flush();
          fl_toggle_pen(&fl_pen);
        }
       if (B_test_pen(c,SG_SPACE))
        /*{{{  skip the used area, do not print the garbage*/
        { x+=sg-1;
          continue;
        }
        /*}}}  */
       /*}}}  */
       c=B_get_char(c);
       over_put((unsigned char)c);
       S_shift_pos(1);
#      ifndef BLOCK_FLUSH
        /*{{{  check skip over equal parts*/
          if (cold && (mi || fl_pen==NORM))
           { int l;

             for (l=x+1;l<=to_over;)
                if ((c=cnew[l])!=cold[l] || (!mi && !B_test_pen(c,NORM)) )
                   break;
                else
                   l++;
             l-=x+1;
             if (l>=FL_SHIFT_OVER)
              { x+=l;
                over_skip(l);
                continue;
              }
           }
        /*}}}  */
#      endif
     }
     /*}}}  */
    over_flush();
    /*{{{  maybe reset pen*/
    if (fl_pen!=NORM)
       fl_toggle_pen(&fl_pen);
    /*}}}  */
    /*{{{  maybe clear rest of line*/
    if (end_space)
       S_moveclreol(y,end_space);
    /*}}}  */
  }
  /*}}}  */
#else
# include "os_scrbuff.c"
#endif

private void fl_upd_line
 ( int y,
   c_p_code * const cold,
   const c_p_code * const cnew,
   boolean full
 )
 {
   /*{{{  variables*/
   int from_txt,to_txt,end_space;
#   ifndef BLOCK_FLUSH
      boolean no_shift;
#   endif
   /*}}}  */
 
   /*{{{  get redraw part of line*/
   /*{{{  init whole line*/
   from_txt=1;
   to_txt=screen.w;
   /*}}}  */
   if (!full)
    /*{{{  try to reduce diff part to a inner block*/
    {
      /*{{{  skip equal part at start*/
      for (;;)
         if (cnew[from_txt]!=cold[from_txt])
            break;
         else if (from_txt==screen.w)
            return;
         else
            from_txt++;
      /*}}}  */
      /*{{{  skip equal part at end*/
      for (;;)
         if (cnew[to_txt]!=cold[to_txt])
             break;
         else
            to_txt--;
      /*}}}  */
    }
    /*}}}  */
   if (am && y==screen.h && to_txt==screen.w)
      to_txt--;
   /*}}}  */
   /*{{{  get whitespace section at end of line*/
   if (ce)
    { for (end_space=screen.w;;)
         if (cnew[end_space]!=SPACE_CODE)
            break;
         else if (--end_space==0)
            break;
      end_space++;
    }
   else
      end_space=screen.w+1;
   /*}}}  */
   /*{{{  check glitches*/
#   ifndef BLOCK_FLUSH
      no_shift=!use_shift || B_scr_mode;
#   endif
   if (!full && B_check_so_glitch)
    { int up;
      int to;

      for (up=1,to=screen.w;;)
         if (B_test_pen(cnew[up],NORM) && B_test_pen(cold[up],NORM))
          /*{{{  next char or end search*/
            if (up==to)
               break;
            else
               up++;
          /*}}}  */
         else
          /*{{{  sorry, but must handle the so glitch :-(*/
          { if (xs)
             /*{{{  use clear-to-end-of-line*/
               S_moveclreol(y,1);
             /*}}}  */
            if (xt)
             /*{{{  use al/dl to get rid of the so*/
             { redraw_on_sig=True;
               S_step_or_move(y,1);
               DelLine(1);
               S_step_or_move(y,1);
               insLine(1);
               redraw_on_sig=False;
               B_move_cursor_to(y,1);
               B_DelLine(1);
               B_move_cursor_to(y,1);
               B_insLine(1);
             }
             /*}}}  */
            /*{{{  complete line without shifting*/
            from_txt=1;
            to_txt=screen.w;
#            ifndef BLOCK_FLUSH
               no_shift=True;
#            endif
            /*}}}  */
            break;
          }
          /*}}}  */
    }
   /*}}}  */
   csr_hide();
   /*{{{  update the line*/
   {
#   ifndef BLOCK_FLUSH
     int shift;

     /*{{{  get shift width*/
     switch (no_shift)
      { case False:
         { int l1,l2;

           l1=to_txt-from_txt;
           l2=(l1+1)/FL_SHIFT_SCALE;
           if (l2)
            { if (l2>FL_SHIFT_MAX)
                 l2=FL_SHIFT_MAX;
              shift=fl_test_shift(cold+from_txt,cnew+from_txt,l1,l2);
              if (shift==0)
                 shift= -fl_test_shift(cnew+from_txt,cold+from_txt,l1,l2);
              break;
            }
         }
        default:
           shift=0;
      }
     /*}}}  */
     redraw_on_sig=True;
     if (shift>0)
      /*{{{  maybe shift right*/
      { if (to_txt<screen.w)
         /*{{{  move and delete*/
         { S_step_or_move(y,to_txt-shift+1);
           delChar(shift);
         }
         /*}}}  */
        S_step_or_move(y,from_txt);
        fl_m_insert(cnew+from_txt,shift);
      }
      /*}}}  */
     else if (shift<0)
      /*{{{  maybe shift left*/
      { S_step_or_move(y,from_txt);
        delChar(-shift);
        if (shift!=-1 || cnew[to_txt]!=SPACE_CODE || to_txt!=end_space)
         /*{{{  insert the chars*/
         { S_step_or_move(y,to_txt+shift+1);
           fl_m_insert(cnew+to_txt+shift+1,-shift);
         }
         /*}}}  */
      }
      /*}}}  */
     else
#   else
        redraw_on_sig=True;
#   endif
        fl_overwrite
         ( y,
           from_txt,
           to_txt,
           end_space,
           cnew
#          ifndef BLOCK_FLUSH
              ,(full && B_scr_mode==OK)?cold:0
#          endif
         );
     redraw_on_sig=False;
     /*{{{  copy new to old*/
     B_copy(cold+from_txt,cnew+from_txt,to_txt-from_txt+1);
     /*}}}  */
   }
   /*}}}  */
 }
/*}}}  */

public void B_oflush(void)
{
  if (!scr_off)
   { if (B_active)
      /*{{{  buffer update*/
        do
         {
           /*{{{  variables*/
           linebuffer *new;
           linebuffer *old;
           /*}}}  */

           csr_push_pos();
           /*{{{  maybe handle signal flush*/
           if (interrupt_restore)
            { B_clr_scr_used=True;
              interrupt_restore=False;
            }
           /*}}}  */
           /*{{{  set new/old to both buffers and change buffers*/
           new=B_current_scr;
           B_toggle_start();
           old=B_current_scr;
           B_update_line_pointer();
           /*}}}  */
           /*{{{  check invalid screen/dl_al usage*/
           if (B_clr_scr_used)
              B_scr_mode=INVALID;
           else if (B_use_dl_al)
              if (FL_NO_DL_AL(B_new_line,screen.h))
                 B_use_dl_al=False;
              else if (B_scr_mode!=OK)
                 B_use_dl_al=False;
           /*}}}  */
           /*{{{  maybe do clrscr first!*/
           if (B_clr_scr_used)
            { csr_hide();
              redraw_on_sig=True;
              S_ClrScr();
              redraw_on_sig=False;
              B_clr_scr_used=False;
            }
           /*}}}  */
           /*{{{  update lines*/
           { int first_diff;
             int first_upd;
             boolean d,a;
             int m_add;

             for
              ( m_add=1,
                first_upd=1,
                a=d=B_use_dl_al,
                first_diff=1+(B_use_dl_al ? 0 : screen.h)
              ; first_upd<=screen.h && !interrupt_restore
              ;
              )
              {
                /*{{{  update all correct scrolled lines*/
                { linebuffer *op,*np;

                  for
                   ( op=old+first_upd,np=new+first_upd
                   ; first_upd<first_diff
                   ; op++,np++,first_upd++
                   )
                   { if
                      (    np->mode!=OK
                        || np->id!=op->id
                        || B_scr_mode!=OK
                      )
                        fl_upd_line
                         ( first_upd,
                           op->code,
                           np->code,
                           B_scr_mode!=OK
                         );
                     op->mode=OK;
                     op->id=np->id=first_upd;
                   }
                }
                /*}}}  */
                /*{{{  try to delete a line*/
                if (d)
                 { int yo,yn;

                   for (yn=yo=first_diff,d=False;yn<=screen.h;)
                    { int idn;

                      if (old[yo].id==(idn=new[yn].id))
                       /*{{{  next(new,old), maybe inc correct shifted part*/
                       { if (yo==first_diff && yn==first_diff)
                            first_diff++;
                         yo+=1;
                         yn+=1;
                       }
                       /*}}}  */
                      else if (idn==0)
                       /*{{{  next new*/
                         yn++;
                       /*}}}  */
                      else
                       /*{{{  do a dl*/
                       { int n;

                         /*{{{  get number of delete lines*/
                         { int ido;
                           int ys;

                           for
                            ( n=1
                            ; (ys=yo+n)<=screen.h && (ido=old[ys].id) && ido!=idn
                            ; n++
                            );
                           if (n>FL_MAX_AL_DL)
                              n=FL_MAX_AL_DL;
                           m_add+=n-1;
                         }
                         /*}}}  */
                         csr_hide();
                         redraw_on_sig=True;
                         S_step_or_move(yo,1);
                         DelLine(n);
                         redraw_on_sig=False;
                         B_move_cursor_to(yo,1);
                         while (n--)
                            B_DelLine(1);
                         d=True;
                         break;
                       }
                       /*}}}  */
                    }
                   /*{{{  no more deletes, so adds are allowed unlimited*/
                   if (!d)
                      m_add=screen.h;
                   /*}}}  */
                 }
                /*}}}  */
                /*{{{  try to add a line*/
                if (a)
                 { int yo,yn;

                   for (yo=yn=first_diff,a=False;yo<=screen.h;)
                    { int ido,idn;

                      if ((ido=old[yo].id)==(idn=new[yn].id))
                       /*{{{  next(new,old), maybe inc correct shifted part*/
                       { if (yo==first_diff && yn==first_diff)
                            first_diff++;
                         yo+=1;
                         yn+=1;
                       }
                       /*}}}  */
                      else if (ido<idn)
                       /*{{{  next old*/
                         yo++;
                       /*}}}  */
                      else
                       /*{{{  do a al*/
                       { int n;

                         /*{{{  get number of add lines*/
                         { int ys;

                           for (n=1;(ys=yn+n)<=screen.h && new[ys].id==0;n++);
                           /*{{{  limit #, to prevent scroll out*/
                           if (n>m_add)
                            { n=m_add;
                              m_add=0;
                            }
                           else
                              m_add-=n;
                           /*}}}  */
                         }
                         /*}}}  */
                         csr_hide();
                         redraw_on_sig=True;
                         S_step_or_move(yo,1);
                         insLine(n);
                         redraw_on_sig=False;
                         B_move_cursor_to(yo,1);
                         while (n--)
                            B_insLine(1);
                         a=True;
                         break;
                       }
                       /*}}}  */
                    }
                 }
                /*}}}  */
              }
           }
           /*}}}  */
           /*{{{  restore and show cursor*/
           csr_pop_pos();
           csr_no_hide();
           /*}}}  */
           /*{{{  screen is now up to date*/
           B_scr_mode=OK;
           B_use_dl_al=al&&dl;
           B_new_line=0;
           /*}}}  */
         }
        while (interrupt_restore);
      /*}}}  */
     oflush;
   }
}
/*}}}  */

/*{{{  B_do_standout/end*/
/*{{{  B_sg*/
private void B_sg(int i,c_p_code c)
{
  do
   { B_cur_line->code[B_pos.w]=c;
     B_inc_w_position(B_pos.w);
   }
  while (--i);
}
/*}}}  */
/*{{{  B_do_standout*/
public void B_do_standout(void)
{
  if (so)
   { B_pen=HIGHLIGHT;
     if (B_active)
      { if (sg)
           B_sg(sg,SO_CODE);
      }
     else
        do_standout();
   }
}
/*}}}  */
/*{{{  B_do_standend*/
public void B_do_standend(void)
{
  if (so)
   { B_pen=NORM;
     if (B_active)
      { if (sg)
           B_sg(sg,SE_CODE);
      }
     else
        do_standend();
   }
}
/*}}}  */
/*}}}  */
/*{{{  B_init_terminal*/
public void B_init_terminal(void)
{
  init_terminal();
  S_invalid_pos();
  B_do_buffer=True;
  B_scr_mode=INVALID;
  B_clr_scr_used=True;
  B_check_active();
}
/*}}}  */
/*{{{  B_reset_terminal*/
public void B_reset_terminal(void)
{
  B_oflush();
  S_invalid_pos();
  reset_terminal();
  B_do_buffer=False;
  B_check_active();
}
/*}}}  */

/*{{{  B_get_terminal_capability*/
public int B_get_terminal_capability(void)
{
  int x;
  win_data old_screen;

  /*{{{  get old size of the screen, or dummy value*/
  if (B_scr_data && B_do_buffer)
     old_screen=screen;
  else
     old_screen.h= -1;
  /*}}}  */
  if ((x=get_terminal_capability()))
     return(x);
  if ((old_screen.h!=screen.h) || (old_screen.w!=screen.w))
   /*{{{  changed sizes, so new buffer*/
   { if (B_scr_data)
        paket_free(B_scr_data);
     if
      /*{{{  can malloc space for 2 buffers*/
      ((B_scr_data=
           paket_malloc
            (   2*screen.w*sizeof(c_p_code)    /* empty and buffer line */
              +   2 * screen.h                 /* 2 buffer: */
                * (  sizeof(linebuffer)           /* line descriptor */
                   + screen.w*sizeof(c_p_code)    /* text field */
                  )
             )
      ))
      /*}}}  */
      /*{{{  start up buffering*/
      { B_get_start(B_buf_id=False);
        /*{{{  init empty and buff line*/
        B_empty_line=(c_p_code*)((linebuffer*)B_scr_data+2*screen.h);
        for
         ( B_buff_line=B_empty_line,x=screen.w
         ; x--
         ; *B_buff_line++=SPACE_CODE
         );
        /*}}}  */
        /*{{{  set up each line buffer*/
        { c_p_code *s;
          linebuffer *l;

          for
           ( l=B_scr_data,x=2*screen.h,s=B_buff_line+screen.w
           ; x
           ; x--,l++,s+=screen.w
           )
           { l->mode=OK;
             if ((l->id=(l-B_scr_data+1))>screen.h)
                l->id-=screen.h;
             B_clr(l->code=s-1,1,screen.w);
           }
        }
        /*}}}  */
        /*{{{  empty screen, cursor at 1/1*/
        B_move_cursor_to(1,1);
        S_ClrScr();
        B_valid();
        B_use_dl_al=al&&dl;
        B_new_line=0;
        /*}}}  */
      }
      /*}}}  */
   }
   /*}}}  */
  B_check_active();
  if (B_scr_data)
   { if ((xs && !ce) || (xt && !B_use_dl_al))
      { so=0;
        allow_text_so=False;
        if (dsp.ctrl==ictrl_dsp)
         { dsp.ctrl=mark_dsp;
           dsp.ctrl_add=0;
         }
      }
     B_check_so_glitch=(so && (sg || xs || xt));
   }
  return(0);
}
/*}}}  */

#ifdef EXT_SCRBUFF_ACCESS
   /*{{{  cur_scr_char*/
   public unsigned char cur_scr_char(int y,int x)
   {
     return
      ( (B_active && x<=screen.w && x>0 && y<=screen.h && y>0)
         ? (unsigned char)B_get_char(B_current_scr[y].code[x])
         : (unsigned char)'\0'
      );
   }
   /*}}}  */
#endif
