summaryrefslogblamecommitdiffstats
path: root/mbbsd/screen.c
blob: 23d00c20c7eb770a1f507468a2a2ddbd7c47e07d (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
          

                
                        
 





                                                          
                                              
                           
                                   
                                           
                                    
 

                                                                             
 
    









































                                                                              
             

                       




                                                                             
    
                        





















                                                                         
    

                  





                                             



                
    



                                        




                                             






                              


                     





                                                                      

                     



                 





















                                             

                                                                        


                     



                                   
                     


                           
           


                                                          



                     


                               

                            
                                          
                        
                        



                            

                               

                   
                                     






                             
           
                                                              



                                     



                                  
                     



                                                  
                     



                                        
    
               
 
                              

                         

              

                                                                     

                             
                              
                                            










                                                          
                                      
                                                                        
             
                
                                              
                          
                                      




                                           
                                           











                                               

















                                          
    
             
 
                                   






               
                                                                  
                                            

                         
                                                       
                    

               

                            
                        




                                        
                              



                                              
                              
     

                                                  


                             


                                                   
                                    
 










                                                         
     


                                                                
              







                                                          

                                             



                              
      
            
                                


                                                   
                                    
                                                                     

                                              
                                                                             
                                  
                                      


                                           
                                        

                                           
                                          
                 

             
                               
                                                                       
                                                                          



                                              






                                               
    
           
 

                               
                      

                
                                
                              

                                               
     

 
    
              
 
                                                  
                       

                  
 
                            

                               











                                                                      

                                                

                                

                                                

                                


                       


                                                     

                    






                                   
                 

 
   
                                          
   
    
                 
 
                               
                         
 

                                                        


                                 
                        
                                   
     
 
 
   
                                  
   
           
              
 
                     

 
    
                     
 
                                                  
                      
 



                                                   















                            

                                 


                                              
                                         


                                                     
                             


                     
      
                                                                              



                                            



                                  
 
                                  
                               
                                    

                                            
                                
                                
                                

                                
     

                          
     
                                                                      

                                                                             

                                                 





                                              
     
      

 
    
                     
 

               

                     
     

 
    









                             







                                                      
























                             
            
 
                
                          




                     
    
             
 
                
                   




                       
    


                                     
 

                       


                     
                                     

               
                                  


                                                     
                     



                                        
                        


              






















                                                  
                           
                         









                                                  
                            
                                      


                       
          
                



                                      
                                  







                                      
                                      
                 
                          
     







                              



                         


                                      



















                                                                

                                           





                                                                       

                                                                         




                                                                   
                                            






                                                     
                                                                

          





                                                                
                                   






                                                                            


                         
                            
 



                                                                                  
 



                                                            

 
                                            


                    
                                

                                           
 



                                                                                  
 


                                                            
     


                          
                
 
 
                               
 
           
   
/* $Id$ */
#include "bbs.h"

#if !defined(USE_PFTERM)

#define o_clear()     output(clearbuf,clearbuflen)
#define o_cleol()     output(cleolbuf,cleolbuflen)
#define o_scrollrev() output(scrollrev,scrollrevlen)
#define o_standup()   output(strtstandout,strtstandoutlen)
#define o_standdown() output(endstandout,endstandoutlen)

static unsigned short cur_ln = 0, cur_col = 0;
static unsigned char docls;
static unsigned char standing = NA;
static int      scrollcnt, tc_col, tc_line;
static unsigned char _typeahead = 1;

#define MODIFIED (1)        /* if line has been modifed, screen output */
#define STANDOUT (2)        /* if this line has a standout region */

void
change_scroll_range(int top, int bottom)
{
    char            buf[16], *p;

    snprintf(buf, sizeof(buf), ESC_STR "[%d;%dr", top + 1, bottom + 1);
    for (p = buf; *p; p++)
    ochar(*p);
}

void
scroll_forward(void)
{
    ochar(ESC_CHR);
    ochar('D');
}

void
do_move(int destcol, int destline)
{
    char            buf[16], *p;

    snprintf(buf, sizeof(buf), ANSI_MOVETO(%d,%d), destline + 1, destcol + 1);
    for (p = buf; *p; p++)
    ochar(*p);
}

void
do_save_cursor(void)
{
    ochar(ESC_CHR);
    ochar('7');
}

void
do_restore_cursor(void)
{
    ochar(ESC_CHR);
    ochar('8');
}


void
initscr(void)
{
    if (!big_picture) {
    big_picture = (screenline_t *) calloc(scr_lns, sizeof(screenline_t));
    docls = YEA;
    }
}

int 
resizeterm(int w, int h)
{
    screenline_t   *new_picture;

    /* make sure reasonable size */
    h = MAX(24, MIN(100, h));
    w = MAX(80, MIN(200, w));

    if (h > t_lines && big_picture) {
    new_picture = (screenline_t *) 
        calloc(h, sizeof(screenline_t));
    if (new_picture == NULL) {
        syslog(LOG_ERR, "calloc(): %m");
        return 0;
    }
    memcpy(new_picture, big_picture, t_lines * sizeof(screenline_t));
    free(big_picture);
    big_picture = new_picture;
    return 1;
    }
    return 0;
}

void
move(int y, int x)
{
    if (y < 0) y = 0;
    if (y >= t_lines) y = t_lines -1;
    if (x < 0) x = 0;
    if (x >= ANSILINELEN) x = ANSILINELEN -1;
    // assert(y>=0);
    // assert(x>=0);
    cur_col = x;
    cur_ln = y;
}

void
move_ansi(int y, int x)
{
    // take ANSI length in consideration
    register screenline_t *slp;
    if (y < 0) y = 0;
    if (y >= t_lines) y = t_lines -1;
    if (x < 0) x = 0;
    if (x >= ANSILINELEN) x = ANSILINELEN -1;

    cur_ln = y;
    cur_col = x;

    if (y >= scr_lns || x < 1)
    return;

    slp = &big_picture[y];
    if (slp->len < 1)
    return;

    slp->data[slp->len] = 0;
    x += (strlen((char*)slp->data) - strlen_noansi((char*)slp->data));
    cur_col = x;
}

void
getyx(int *y, int *x)
{
    *y = cur_ln;
    *x = cur_col;
}

void
getyx_ansi(int *py, int *px)
{
    // take ANSI length in consideration
    register screenline_t *slp;
    int y = cur_ln,  x = cur_col;
    char c = 0;

    if (y < 0) y = 0;
    if (y >= t_lines) y = t_lines -1;
    if (x < 0) x = 0;
    if (x >= ANSILINELEN) x = ANSILINELEN -1;

    *py = y; *px = x;

    if (y >= scr_lns || x < 1)
    return;

    slp = &big_picture[y];
    if (slp->len < 1)
    return;
    c = slp->data[x];
    slp->data[x] = 0;
    *px -= (strlen((char*)slp->data) - strlen_noansi((char*)slp->data));
    slp->data[x] = c;
}

static inline
screenline_t* GetCurrentLine(){
    register int i = cur_ln + roll;
    if(i >= scr_lns)
    i %= scr_lns;
    return &big_picture[i];
}

static void
rel_move(int was_col, int was_ln, int new_col, int new_ln)
{
    if (new_ln >= t_lines || new_col >= t_columns)
    return;

    tc_col = new_col;
    tc_line = new_ln;
    if (new_col == 0) {
    if (new_ln == was_ln) {
        if (was_col)
        ochar('\r');
        return;
    } else if (new_ln == was_ln + 1) {
        ochar('\n');
        if (was_col)
        ochar('\r');
        return;
    }
    }
    if (new_ln == was_ln) {
    if (was_col == new_col)
        return;

    if (new_col == was_col - 1) {
        ochar(Ctrl('H'));
        return;
    }
    }
    do_move(new_col, new_ln);
}

static void
standoutput(const char *buf, int ds, int de, int sso, int eso)
{
    int             st_start, st_end;

    if (eso <= ds || sso >= de) {
    output(buf + ds, de - ds);
    } else {
    st_start = MAX(sso, ds);
    st_end = MIN(eso, de);
    if (sso > ds)
        output(buf + ds, sso - ds);
    o_standup();
    output(buf + st_start, st_end - st_start);
    o_standdown();
    if (de > eso)
        output(buf + eso, de - eso);
    }
}

void
redrawwin(void)
{
    register screenline_t *bp;
    register int    i, j;
    int len;

    o_clear();
    for (tc_col = tc_line = i = 0, j = roll; i < scr_lns; i++, j++) {
    if (j >= scr_lns)
        j = 0;
    bp = &big_picture[j];
    if ((len = bp->len)) {
        rel_move(tc_col, tc_line, 0, i);

#ifdef DBCSAWARE
        if (!(bp->mode & STANDOUT) &&
            (cuser.uflag & DBCS_NOINTRESC) &&
            DBCS_RemoveIntrEscape(bp->data, &len))
        {
        // if anything changed, dirty whole line.
        bp->len = len;
        }
#endif // DBCSAWARE

        if (bp->mode & STANDOUT) {
        standoutput((char *)bp->data, 0, len, bp->sso, bp->eso);
        }
        else
        output((char *)bp->data, len);
        tc_col += len;
        if (tc_col >= t_columns) {
        if (automargins)
            tc_col = t_columns - 1;
        else {
            tc_col -= t_columns;
            tc_line++;
            if (tc_line >= t_lines)
            tc_line = b_lines;
        }
        }
        bp->mode &= ~(MODIFIED);
        bp->oldlen = len;
    }
    }
    rel_move(tc_col, tc_line, cur_col, cur_ln);
    docls = scrollcnt = 0;
    oflush();
}

int 
typeahead(int fd)
{
    switch(fd)
    {
    case TYPEAHEAD_NONE:
        _typeahead = 0;
        break;
    case TYPEAHEAD_STDIN:
        _typeahead = 1;
        break;
    default: // shall never reach here
        assert(NULL);
        break;
    }
    return 0;
}

void
refresh(void)
{
    if (num_in_buf() && _typeahead)
    return;
    doupdate();
}

void
doupdate(void)
{
    /* TODO remove unnecessary refresh() call, to save CPU time */
    register screenline_t *bp = big_picture;
    register int    i, j;
    int len;
    if ((docls) || (abs(scrollcnt) >= (scr_lns - 3))) {
    redrawwin();
    return;
    }
    if (scrollcnt < 0) {
    if (!scrollrevlen) {
        redrawwin();
        return;
    }
    rel_move(tc_col, tc_line, 0, 0);
    do {
        o_scrollrev();
    } while (++scrollcnt);
    } else if (scrollcnt > 0) {
    rel_move(tc_col, tc_line, 0, b_lines);
    do {
        ochar('\n');
    } while (--scrollcnt);
    }
    for (i = 0, j = roll; i < scr_lns; i++, j++) {
    if (j >= scr_lns)
        j = 0;
    bp = &big_picture[j];
    len = bp->len;

    if (bp->mode & MODIFIED && bp->smod < len) 
    {
        bp->mode &= ~(MODIFIED);

#ifdef DBCSAWARE
        if (!(bp->mode & STANDOUT) &&
        (cuser.uflag & DBCS_NOINTRESC) &&
        DBCS_RemoveIntrEscape(bp->data, &len))
        {
        // if anything changed, dirty whole line.
        bp->len = len;
        bp->smod = 0; bp->emod = len;
        }
#endif // DBCSAWARE

#if 0
        // disabled now, bugs: 
        // (1) input number (goto) in bbs list (search_num)
        // (2) some empty lines becomes weird (eg, b_config)
        //
        // more effort to determine ANSI smod
        if (bp->smod > 0)
        {
        int iesc;
        for (iesc = bp->smod-1; iesc >= 0; iesc--)
        {
            if (bp->data[iesc] == ESC_CHR)
            {
            bp->smod = 0;// iesc;
            bp->emod =len -1;
            break;
            }
        }
        }
#endif
        
        if (bp->emod >= len)
        bp->emod = len - 1;
        rel_move(tc_col, tc_line, bp->smod, i);

        if (bp->mode & STANDOUT)
        standoutput((char *)bp->data, bp->smod, bp->emod + 1,
                bp->sso, bp->eso);
        else
        output((char *)&bp->data[bp->smod], bp->emod - bp->smod + 1);
        tc_col = bp->emod + 1;
        if (tc_col >= t_columns) {
        if (automargins)
            tc_col = t_columns - 1;
        else {
            tc_col -= t_columns;
            tc_line++;
            if (tc_line >= t_lines)
            tc_line = b_lines;
        }
        }
    }
    if (bp->oldlen > len) {
        /* XXX len/oldlen also count the length of escape sequence,
         * before we fix it, we must print ANSI_CLRTOEND everywhere */
        rel_move(tc_col, tc_line, len, i);
        o_cleol();
    }
    bp->oldlen = len;
    }

    rel_move(tc_col, tc_line, cur_col, cur_ln);

    oflush();
}

void
clear(void)
{
    register screenline_t *slp;

    register int    i;

    docls = YEA;
    cur_col = cur_ln = roll = 0;
    for(i=0; i<scr_lns; i++) {
    slp = &big_picture[i];
    slp->mode = slp->len = slp->oldlen = 0;
    }
}

void
clrtoeol(void)
{
    register screenline_t *slp = GetCurrentLine();
    register int    ln;

    standing = NA;

    if (cur_col <= slp->sso)
    slp->mode &= ~STANDOUT;

    /*
    if (cur_col == 0) // TODO and contains ANSI
    {
    // workaround poor ANSI issue
    size_t sz = (slp->len > slp->oldlen) ? slp->len : slp->oldlen;
    sz = (sz < ANSILINELEN) ? sz : ANSILINELEN;
    memset(slp->data, ' ', sz);
    slp->len = 0;
    return;
    }
    */

    if (cur_col > slp->oldlen) {
    for (ln = slp->len; ln <= cur_col; ln++)
        slp->data[ln] = ' ';
    }
    if (cur_col < slp->oldlen) {
    for (ln = slp->len; ln >= cur_col; ln--)
        slp->data[ln] = ' ';
    }
    slp->len = cur_col;
}


void newwin (int nlines, int ncols, int y, int x)
{
    int i=0, oy, ox;
    getyx(&oy, &ox);

    while (nlines-- > 0)
    {
    move_ansi(y++, x);
    for (i = 0; i < ncols; i++)
        outc(' ');
    }
    move(oy, ox);
}

/**
 * 從目前的行數(scr_ln) clear 到第 line 行
 */
void
clrtoln(int line)
{
    register screenline_t *slp;
    register int    i, j;

    for (i = cur_ln, j = i + roll; i < line; i++, j++) {
    if (j >= scr_lns)
        j -= scr_lns;
    slp = &big_picture[j];
    slp->mode = slp->len = 0;
    if (slp->oldlen)
        slp->oldlen = scr_cols;
    }
}

/**
 * 從目前的行數(scr_ln) clear 到底
 */
inline void
clrtobot(void)
{
    clrtoln(scr_lns);
}

void
outc(unsigned char c)
{
    register screenline_t *slp = GetCurrentLine();
    register int    i;

    // 0xFF is invalid for most cases (even DBCS), 
    if (c == 0xFF || c == 0x00)
    return;

    if (c == '\t') {
    int x, y;

    getyx_ansi(&y, &x);

    if (x % 8 == 0)
        i = 8;
    else
        i = 8 - (x % 8);

    for (;i > 0; i--)
        outc(' ');

    return;
    }

    if (c == '\n' || c == '\r') {
    if (standing) {
        slp->eso = MAX(slp->eso, cur_col);
        standing = NA;
    }
    if ((i = cur_col - slp->len) > 0)
        memset(&slp->data[slp->len], ' ', i + 1);
    slp->len = cur_col;
    cur_col = 0;
    if (cur_ln < scr_lns)
        cur_ln++;
    return;
    }
    /*
     * else if(c != ESC_CHR && !isprint2(c)) { c = '*'; //substitute a '*' for
     * non-printable }
     */
    if (cur_col >= slp->len) {
    for (i = slp->len; i < cur_col; i++)
        slp->data[i] = ' ';
    slp->data[cur_col] = '\0';
    slp->len = cur_col + 1;
    }

    if (slp->data[cur_col] != c) {
    slp->data[cur_col] = c;
    if (!(slp->mode & MODIFIED))
        slp->smod = slp->emod = cur_col;
    slp->mode |= MODIFIED;
    if (cur_col > slp->emod)
        slp->emod = cur_col;
    if (cur_col < slp->smod)
        slp->smod = cur_col;
    }
#if 1
    if(cur_col < scr_cols)
        cur_col++;
#else
    /* vvv commented by piaip: but SCR_COLS is 511 > unsigned char! */
    /* this comparison is always false (cur_col is unsigned char and scr_cols
     * is 511). */
    if (++cur_col >= scr_cols) {
    if (standing && (slp->mode & STANDOUT)) {
        standing = 0;
        slp->eso = MAX(slp->eso, cur_col);
    }
    cur_col = 0;
    if (cur_ln < scr_lns)
        cur_ln++;
    }
#endif
}

void
outs(const char *str)
{
    if (!str)
    return;
    while (*str) {
    outc(*str++);
    }
}

void
outns(const char *str, int n)
{
    if (!str)
    return;
    while (*str && n-- > 0) {
    outc(*str++);
    }
}

void
outstr(const char *str)
{
    // XXX TODO cannot prepare DBCS-ready environment?
    
    outs(str);
}

void
addch(unsigned char c)
{
    outc(c);
}

void
addstr(const char *s)
{
    outs(s);
}

void
addnstr(const char *s, int n)
{
    outns(s, n);
}

void
addstring(const char *s)
{
    outs(s);
}


void
scroll(void)
{
    scrollcnt++;
    if (++roll >= scr_lns)
    roll = 0;
    move(b_lines, 0);
    clrtoeol();
}

void
rscroll(void)
{
    scrollcnt--;
    if (--roll < 0)
    roll = b_lines;
    move(0, 0);
    clrtoeol();
}

void
region_scroll_up(int top, int bottom)
{
    int             i;

    if (top > bottom) {
    i = top;
    top = bottom;
    bottom = i;
    }
    if (top < 0 || bottom >= scr_lns)
    return;

    for (i = top; i < bottom; i++)
    big_picture[i] = big_picture[i + 1];
    memset(big_picture + i, 0, sizeof(*big_picture));
    memset(big_picture[i].data, ' ', scr_cols);
    do_save_cursor();
    change_scroll_range(top, bottom);
    do_move(0, bottom);
    scroll_forward();
    change_scroll_range(0, scr_lns - 1);
    do_restore_cursor();
    refresh();
}

// readback
int     
instr(char *str)
{
    register screenline_t *slp = GetCurrentLine();
    *str = 0;
    if (!slp)
    return 0;
    slp->data[slp->len] = 0;
    strip_ansi(str, (char*)slp->data, STRIP_ALL);
    return strlen(str);
}

int     
innstr(char *str, int n)
{
    register screenline_t *slp = GetCurrentLine();
    char buf[ANSILINELEN];
    *str = 0;
    if (!slp)
    return 0;
    slp->data[slp->len] = 0;
    strip_ansi(buf, (char*)slp->data, STRIP_ALL);
    buf[ANSILINELEN-1] = 0;
    strlcpy(str, buf, n);
    return strlen(str);
}

int 
inansistr(char *str, int n)
{
    register screenline_t *slp = GetCurrentLine();
    *str = 0;
    if (!slp)
    return 0;
    slp->data[slp->len] = 0;
    strlcpy(str, (char*)slp->data, n);
    return strlen(str);
}

// level: 
// -1 - bold out
//  0 - dark text
//  1 - text
//  2 - no highlight (not implemented)
void
grayout(int y, int end, int level)
{
    register screenline_t *slp = NULL;
    char buf[ANSILINELEN];
    int i = 0;

    if (y < 0) y = 0;
    if (end > b_lines) end = b_lines;

    // TODO change to y <= end someday
    // loop lines
    for (; y <= end; y ++)
    {
    // modify by scroll
    i = y + roll;
    if (i < 0)
        i += scr_lns;
    else if (i >= scr_lns)
        i %= scr_lns;

    slp = &big_picture[i];

    if (slp->len < 1)
        continue;

    if (slp->len >= ANSILINELEN)
        slp->len = ANSILINELEN -1;

    // tweak slp
    slp->data[slp->len] = 0;
    slp->mode &= ~STANDOUT;
    slp->mode |= MODIFIED;
    slp->oldlen = 0;
    slp->sso = slp->eso = 0;
    slp->smod = 0;

    // make slp->data a pure string
    for (i=0; i<slp->len; i++)
    {
        if (!slp->data[i])
        slp->data[i] = ' ';
    }

    slp->len = strip_ansi(buf, (char*)slp->data, STRIP_ALL);
    buf[slp->len] = 0;

    switch(level)
    {
        case GRAYOUT_DARK: // dark text
        case GRAYOUT_BOLD:// bold text
        // basically, in current system slp->data will
        // not exceed t_columns. buffer overflow is impossible.
        // but to make it more robust, let's quick check here.
        // of course, t_columns should always be far smaller.
        if (strlen((char*)slp->data) > t_columns)
            slp->data[t_columns] = 0;
        strcpy((char*)slp->data, 
            level < 0 ? ANSI_COLOR(1) : ANSI_COLOR(1;30;40));
        strcat((char*)slp->data, buf);
        strcat((char*)slp->data, ANSI_RESET ANSI_CLRTOEND);
        slp->len = strlen((char*)slp->data);
        break;

        case GRAYOUT_NORM: // Plain text
        memcpy(slp->data, buf, slp->len + 1);
        break;
    }
    slp->emod = slp->len -1;
    }
}

static size_t screen_backupsize(int len, const screenline_t *bp)
{
    int i;
    size_t sum = 0;
    for(i = 0; i < len; i++)
    sum += ((char*)&bp[i].data - (char*)&bp[i]) + bp[i].len;
    return sum;
}

void scr_dump(screen_backup_t *old)
{
    int i;
    size_t offset = 0;
    void *buf;
    screenline_t* bp = big_picture;

    buf = old->raw_memory = malloc(screen_backupsize(t_lines, big_picture));

    old->col = t_columns;
    old->row = t_lines;
    getyx(&old->y, &old->x);

    for(i = 0; i < t_lines; i++) {
    /* backup header */
    memcpy((char*)buf + offset, &bp[i], ((char*)&bp[i].data - (char*)&bp[i]));
    offset += ((char*)&bp[i].data - (char*)&bp[i]);

    /* backup body */
    memcpy((char*)buf + offset, &bp[i].data, bp[i].len);
    offset += bp[i].len;
    }
}

void scr_restore(const screen_backup_t *old)
{
    int i;
    size_t offset=0;
    void *buf = old->raw_memory;
    screenline_t* bp = big_picture;
    const int len = MIN(old->row, t_lines);

    for(i = 0; i < len; i++) {
    /* restore header */
    memcpy(&bp[i], (char*)buf + offset, ((char*)&bp[i].data - (char*)&bp[i]));
    offset += ((char*)&bp[i].data - (char*)&bp[i]);

    /* restore body */
    memcpy(&bp[i].data, (char*)buf + offset, bp[i].len);
    offset += bp[i].len;
    }

    free(old->raw_memory);
    move(old->y, old->x);
    redrawwin();
}

#endif //  !defined(USE_PFTERM)

/* vim:sw=4
 */