/* $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]; *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; imode = 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; ilen; 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 */