/* * Copyright 1992 Eric R. Smith. All rights reserved. * Redistribution is permitted only if the distribution * is not for profit, and only if all documentation * (including, in particular, the file "copying") * is included in the distribution in unmodified form. * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN * RISK. */ /* * VT52 emulator for a GEM text window (with type TEXTWIN) */ #include "xgem.h" #include #include #include "twdefs.h" #include "twproto.h" void vt52_putch(); /* * paint(v, c): put character 'c' at the current (x, y) coordinates * of window v. If insert mode is on, this involves moving all the * other characters one space to the left, first. */ static void paint(v, c) TEXTWIN *v; int c; { int i; int line = v->cy; if (v->term_flags & FINSERT) { for (i = v->maxx-1; i > v->cx; --i) { v->data[line][i] = v->data[line][i-1]; v->cflag[line][i] = v->cflag[line][i-1] | CDIRTY; } } if (v->data[line][v->cx] != c) { v->data[line][v->cx] = c; v->cflag[line][v->cx] = CDIRTY | v->term_cattr; } else v->cflag[line][v->cx] = (CTOUCHED | v->term_cattr); v->dirty[line] |= SOMEDIRTY; } /* * gotoxy (v, x, y): move current cursor address of window v to (x, y) * verifies that (x,y) is within range */ static void gotoxy(v, x, y) TEXTWIN *v; int x, y; { if (x < 0) x = 0; else if (x >= v->maxx) x = v->maxx - 1; if (y < v->miny) y = v->miny; else if (y >= v->maxy) y = v->maxy - 1; v->cx = x; v->cy = y; } /* * clrline(v, r): clear line r of window v */ static void clrline(v, r) TEXTWIN *v; int r; { int i; for (i = v->maxx-1; i >= 0; --i) { v->data[r][i] = ' '; v->cflag[r][i] = v->term_cattr & (CBGCOL|CFGCOL); } v->dirty[r] = ALLDIRTY; } /* * clear(v): clear the whole window v */ static void clear(v) TEXTWIN *v; { int y; for (y = v->miny; y < v->maxy; y++) clrline(v, y); } /* * clrchar(v, x, y): clear the (x,y) position on window v */ static void clrchar(v, x, y) TEXTWIN *v; int x, y; { if (v->data[y][x] != ' ') { v->data[y][x] = ' '; v->cflag[y][x] = CDIRTY | (v->term_cattr & (CBGCOL|CFGCOL)); } else { v->cflag[y][x] = CTOUCHED | (v->term_cattr & (CBGCOL|CFGCOL)); } v->dirty[y] |= SOMEDIRTY; } /* * clrfrom(v, x1, y1, x2, y2): clear window v from position (x1,y1) to * position (x2, y2) inclusive. It is assumed that y2 >= y1. */ static void clrfrom(v, x1, y1, x2, y2) TEXTWIN *v; int x1,y1,x2,y2; { int i; for (i = x1; i < v->maxx; i++) clrchar(v, i, y1); if (y2 > y1) { for (i = 0; i <= x2; i++) clrchar(v, i, y2); for (i = y1+1; i < y2; i++) clrline(v, i); } } /* * delete_char(v, x, y): delete the character at position (x, y) on * the screen; the rest of the line is scrolled left, and a blank is * inserted at the end of the line. */ static void delete_char(v, x, y) TEXTWIN *v; int x, y; { int i; for (i = x; i < v->maxx-1; i++) { v->data[y][i] = v->data[y][i+1]; v->cflag[y][i] = v->cflag[y][i+1] | CDIRTY; } v->data[y][v->maxx-1] = ' '; v->cflag[y][v->maxx-1] = CDIRTY | (v->term_cattr & (CBGCOL|CFGCOL)); v->dirty[y] |= SOMEDIRTY; } /* * delete_line(v, r): delete line r of window v. The screen below this * line is scrolled up, and the bottom line is cleared. */ #define scroll(v) delete_line(v, 0) static void delete_line(v, r) TEXTWIN *v; int r; { int y; int doscroll = (r == 0); UCHAR *oldline; short *oldflag; oldline = v->data[r]; oldflag = v->cflag[r]; for (y = r; y < v->maxy-1; y++) { v->data[y] = v->data[y+1]; v->cflag[y] = v->cflag[y+1]; v->dirty[y] = doscroll ? v->dirty[y+1] : ALLDIRTY; } v->data[y] = oldline; v->cflag[y] = oldflag; /* clear the last line */ clrline(v, v->maxy - 1); if (doscroll) { v->scrolled++; } } /* * insert_line(v, r): scroll all of the window from line r down, * and then clear line r. */ static void insert_line(v, r) TEXTWIN *v; int r; { int i, limit; UCHAR *oldline; short *oldflag; limit = v->maxy - 1; oldline = v->data[limit]; oldflag = v->cflag[limit]; for (i = limit-1; i >= r ; --i) { /* move line i to line i+1 */ v->data[i+1] = v->data[i]; v->cflag[i+1] = v->cflag[i]; v->dirty[i+1] = ALLDIRTY; } v->cflag[r] = oldflag; v->data[r] = oldline; /* clear line r */ clrline(v, r); } /* * assorted callback functions */ static void set_title(v) TEXTWIN *v; { WINDOW *w = v->win; title_window(w, v->captbuf); } static void set_size(v) TEXTWIN *v; { int rows, cols; char *s; s = v->captbuf; while (*s && *s != ',') s++; if (*s) *s++ = 0; cols = decval(v->captbuf); rows = decval(s); if (rows == 0) rows = v->maxy; else if (rows < MINROWS) rows = MINROWS; else if (rows > MAXROWS) rows = MAXROWS; if (cols == 0) cols = v->maxx; else if (rows < MINCOLS) rows = MINCOLS; else if (cols > MAXCOLS) cols = MAXCOLS; resize_textwin(v, cols, rows, v->miny); } /* capture(v, c): put character c into the capture buffer * if c is '\r', then we're finished and we call the callback * function */ static void capture(v, c) TEXTWIN *v; int c; { int i = v->captsiz; if (c == '\r') c = 0; if (i < CAPTURESIZE || c == 0) { v->captbuf[i++] = c; v->captsiz = i; } if (c == 0) { v->output = vt52_putch; (*v->callback)(v); } } /* * paint a character, even if it's a graphic character */ static void quote_putch(v, c) TEXTWIN *v; int c; { if (c == 0) c = ' '; curs_off(v); paint(v, c); v->cx++; if (v->cx == v->maxx) { if (v->term_flags & FWRAP) { v->cx = 0; vt52_putch(v, '\n'); } else { v->cx = v->maxx - 1; } } curs_on(v); v->output = vt52_putch; } static void fgcol_putch(v, c) TEXTWIN *v; int c; { v->term_cattr = (v->term_cattr & ~CFGCOL) | ((c & 0x000f) << 4); v->output = vt52_putch; } static void bgcol_putch(v, c) TEXTWIN *v; int c; { v->term_cattr = (v->term_cattr & ~CBGCOL) | (c & 0x000f); v->output = vt52_putch; } /* set special effects */ static void seffect_putch(v, c) TEXTWIN *v; int c; { v->term_cattr |= ((c & 0x1f) << 8); v->output = vt52_putch; } /* clear special effects */ static void ceffect_putch(v, c) TEXTWIN *v; int c; { v->term_cattr &= ~((c & 0x1f) << 8); v->output = vt52_putch; } /* set cursor flash time */ static void timer_putch(v, c) TEXTWIN *v; int c; { c -= ' '; if (c < 0) c = 0; v->flashperiod = v->flashtimer = c; v->output = vt52_putch; } /* * putesc(v, c): handle the control sequence ESC c */ static void putesc(v, c) TEXTWIN *v; int c; { int cx, cy; static void escy_putch(); curs_off(v); cx = v->cx; cy = v->cy; switch (c) { case 'A': /* cursor up */ gotoxy(v, cx, cy-1); break; case 'B': /* cursor down */ gotoxy(v, cx, cy+1); break; case 'C': /* cursor right */ gotoxy(v, cx+1, cy); break; case 'D': /* cursor left */ gotoxy(v, cx-1, cy); break; case 'E': /* clear home */ clear(v); /* fall through... */ case 'H': /* cursor home */ gotoxy(v, 0, v->miny); break; case 'I': /* cursor up, insert line */ if (cy == v->miny) insert_line(v, v->miny); else gotoxy(v, cx, cy-1); break; case 'J': /* clear below cursor */ clrfrom(v, cx, cy, v->maxx-1, v->maxy-1); break; case 'K': /* clear remainder of line */ clrfrom(v, cx, cy, v->maxx-1, cy); break; case 'L': /* insert a line */ insert_line(v, cy); gotoxy(v, 0, cy); break; case 'M': /* delete line */ delete_line(v, cy); gotoxy(v, 0, cy); break; case 'Q': /* MW extension: quote next character */ v->output = quote_putch; curs_on(v); return; case 'R': /* TW extension: set window size */ v->captsiz = 0; v->output = capture; v->callback = set_size; curs_on(v); return; case 'S': /* MW extension: set title bar */ v->captsiz = 0; v->output = capture; v->callback = set_title; curs_on(v); return; case 'T': /* TW extension: send termcap string */ output_termcap(v); break; case 'Y': v->output = escy_putch; curs_on(v); return; case 'a': /* MW extension: delete character */ delete_char(v, cx, cy); break; case 'b': /* set foreground color */ v->output = fgcol_putch; curs_on(v); return; /* `return' to avoid resetting v->output */ case 'c': /* set background color */ v->output = bgcol_putch; curs_on(v); return; case 'd': /* clear to cursor position */ clrfrom(v, 0, v->miny, cx, cy); break; case 'e': /* enable cursor */ v->term_flags |= FCURS; break; case 'f': /* cursor off */ v->term_flags &= ~FCURS; break; case 'h': /* MW extension: enter insert mode */ v->term_flags |= FINSERT; break; case 'i': /* MW extension: leave insert mode */ v->term_flags &= ~FINSERT; break; case 'j': /* save cursor position */ v->savex = v->cx; v->savey = v->cy; break; case 'k': /* restore saved position */ gotoxy(v, v->savex, v->savey); break; case 'l': /* clear line */ clrline(v, cy); gotoxy(v, 0, cy); break; case 'o': /* clear from start of line to cursor */ clrfrom(v, 0, cy, cx, cy); break; case 'p': /* reverse video on */ v->term_cattr |= CINVERSE; break; case 'q': /* reverse video off */ v->term_cattr &= ~CINVERSE; break; case 't': /* TW extension: set cursor timer */ v->flashtimer = v->flashperiod = 0; v->output = timer_putch; curs_on(v); return; case 'u': /* TW extension: cursor flashing OFF */ v->flashtimer = v->flashperiod = 0; break; case 'v': /* wrap on */ v->term_flags |= FWRAP; break; case 'w': v->term_flags &= ~FWRAP; break; case 'y': /* TW extension: set special effects */ v->output = seffect_putch; curs_on(v); return; case 'z': /* TW extension: clear special effects */ v->output = ceffect_putch; curs_on(v); return; } v->output = vt52_putch; curs_on(v); } /* * escy1_putch(v, c): for when an ESC Y + char has been seen */ static void escy1_putch(v, c) TEXTWIN *v; int c; { static void vt52_putch(); curs_off(v); gotoxy(v, c - ' ', v->miny + v->escy1 - ' '); v->output = vt52_putch; curs_on(v); } /* * escy_putch(v, c): for when an ESC Y has been seen */ static void escy_putch(v, c) TEXTWIN *v; int c; { curs_off(v); v->escy1 = c; v->output = escy1_putch; curs_on(v); } /* * vt52_putch(v, c): put character 'c' on screen 'v'. This is the default * for when no escape, etc. is active */ void vt52_putch(v, c) TEXTWIN *v; int c; { int cx, cy; cx = v->cx; cy = v->cy; curs_off(v); c &= 0x00ff; /* control characters */ if (c < ' ') { switch (c) { case '\r': gotoxy(v, 0, cy); curs_on(v); return; case '\n': if (cy == v->maxy - 1) { curs_off(v); scroll(v); } else gotoxy(v, cx, cy+1); curs_on(v); return; case '\b': gotoxy(v, cx-1, cy); curs_on(v); return; case '\007': /* BELL */ (void)Bconout(2, 7); curs_on(v); return; case '\033': /* ESC */ v->output = putesc; curs_on(v); return; case '\t': gotoxy(v, (v->cx +8) & ~7, v->cy); /* fall through */ default: curs_on(v); return; } } paint(v, c); v->cx++; if (v->cx == v->maxx) { if (v->term_flags & FWRAP) { v->cx = 0; vt52_putch(v, '\n'); } else { v->cx = v->maxx - 1; } } curs_on(v); } /* routines for setting the cursor state in window v */ void set_curs(v, on) TEXTWIN *v; int on; { if (on && (v->term_flags & FCURS) && !(v->term_flags & FFLASH)) { if (focuswin && v == focuswin->extra) v->cflag[v->cy][v->cx] ^= CINVERSE; else v->cflag[v->cy][v->cx] ^= CE_UNDERLINE; v->cflag[v->cy][v->cx] |= CTOUCHED; v->dirty[v->cy] |= SOMEDIRTY; v->term_flags |= FFLASH; } else if ( (!on) && (v->term_flags & FFLASH)) { if (focuswin && v == focuswin->extra) v->cflag[v->cy][v->cx] ^= CINVERSE; else v->cflag[v->cy][v->cx] ^= CE_UNDERLINE; v->cflag[v->cy][v->cx] |= CTOUCHED; v->dirty[v->cy] |= SOMEDIRTY; v->term_flags &= ~FFLASH; } v->flashtimer = v->flashperiod; } /* flash the cursor in the indicated window */ void curs_flash(v) TEXTWIN *v; { if (v->term_flags & FCURS) { if (focuswin && v == focuswin->extra) v->cflag[v->cy][v->cx] ^= CINVERSE; else v->cflag[v->cy][v->cx] ^= CE_UNDERLINE; v->dirty[v->cy] |= SOMEDIRTY; v->cflag[v->cy][v->cx] |= CTOUCHED; v->term_flags ^= FFLASH; } v->flashtimer = v->flashperiod; }