diff options
Diffstat (limited to 'console/screen.c')
-rw-r--r-- | console/screen.c | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/console/screen.c b/console/screen.c new file mode 100644 index 00000000..8b518070 --- /dev/null +++ b/console/screen.c @@ -0,0 +1,819 @@ +/* $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 +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]; + *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 == '\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); + save_cursor(); + change_scroll_range(top, bottom); + do_move(0, bottom); + scroll_forward(); + change_scroll_range(0, scr_lns - 1); + restore_cursor(); + refresh(); +} + +void +standout(void) +{ + if (!standing && strtstandoutlen) { + register screenline_t *slp; + + slp = GetCurrentLine(); + standing = YEA; + slp->sso = slp->eso = cur_col; + slp->mode |= STANDOUT; + } +} + +void +standend(void) +{ + if (standing && strtstandoutlen) { + register screenline_t *slp; + + slp = GetCurrentLine(); + standing = NA; + slp->eso = MAX(slp->eso, cur_col); + } +} + +// 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] = 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 + */ |