/* Copyright (c) 2008, 2009 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Micah Cowan (micah@cowan.name) * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, see * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * **************************************************************** */ #include #include #ifndef sun /* we want to know about TIOCPKT. */ # include #endif #include "config.h" #include "screen.h" #include "extern.h" #include "logfile.h" extern struct display *display, *displays; extern struct win *fore; /* for 83 escape */ extern struct layer *flayer; /* for 83 escape */ extern struct NewWindow nwin_default; /* for ResetWindow() */ extern int nversion; /* numerical version of screen */ extern int log_flush, logtstamp_on, logtstamp_after; extern char *logtstamp_string; extern char *captionstring; extern char *hstatusstring; extern char *wliststr; extern int compacthist; extern struct acluser *EffectiveAclUser; int Z0width, Z1width; /* widths for Z0/Z1 switching */ /* globals set in WriteString */ static struct win *curr; /* window we are working on */ static int rows, cols; /* window size of the curr window */ int visual_bell = 0; int use_hardstatus = 1; /* display status line in hs */ char *printcmd = 0; int use_altscreen = 0; /* enable alternate screen support? */ unsigned char *blank; /* line filled with spaces */ unsigned char *null; /* line filled with '\0' */ struct mline mline_old; struct mline mline_blank; struct mline mline_null; struct mchar mchar_null; struct mchar mchar_blank = {' ' /* , 0, 0, ... */}; struct mchar mchar_so = {' ', A_SO /* , 0, 0, ... */}; int renditions[NUM_RENDS] = {65529 /* =ub */, 65531 /* =b */, 65533 /* =u */ }; /* keep string_t and string_t_string in sync! */ static char *string_t_string[] = { "NONE", "DCS", /* Device control string */ "OSC", /* Operating system command */ "APC", /* Application program command */ /* - used for status change */ "PM", /* Privacy message */ "AKA", /* title for current screen */ "GM", /* Global message to every display */ "STATUS" /* User hardstatus line */ }; /* keep state_t and state_t_string in sync! */ static char *state_t_string[] = { "LIT", /* Literal input */ "ESC", /* Start of escape sequence */ "ASTR", /* Start of control string */ "STRESC", /* ESC seen in control string */ "CSI", /* Reading arguments in "CSI Pn ;...*/ "PRIN", /* Printer mode */ "PRINESC", /* ESC seen in printer mode */ "PRINCSI", /* CSI seen in printer mode */ "PRIN4" /* CSI 4 seen in printer mode */ }; static int Special (int); static void DoESC (int, int); static void DoCSI (int, int); static void StringStart (enum string_t); static void StringChar (int); static int StringEnd (void); static void PrintStart (void); static void PrintChar (int); static void PrintFlush (void); static void DesignateCharset (int, int); static void MapCharset (int); static void MapCharsetR (int); static void SaveCursor (struct cursor *); static void RestoreCursor (struct cursor *); static void BackSpace (void); static void Return (void); static void LineFeed (int); static void ReverseLineFeed (void); static void InsertChar (int); static void DeleteChar (int); static void DeleteLine (int); static void InsertLine (int); static void Scroll (char *, int, int, char *); static void ForwardTab (void); static void BackwardTab (void); static void ClearScreen (void); static void ClearFromBOS (void); static void ClearToEOS (void); static void ClearLineRegion (int, int); static void CursorRight (int); static void CursorUp (int); static void CursorDown (int); static void CursorLeft (int); static void ASetMode (int); static void SelectRendition (void); static void RestorePosRendition (void); static void FillWithEs (void); static void FindAKA (void); static void Report (char *, int, int); static void ScrollRegion (int); static void WAddLineToHist (struct win *, struct mline *); static void WLogString (struct win *, char *, int); static void WReverseVideo (struct win *, int); static int WindowChangedCheck (char *, int, int *); static void MFixLine (struct win *, int, struct mchar *); static void MScrollH (struct win *, int, int, int, int, int); static void MScrollV (struct win *, int, int, int, int); static void MClearArea (struct win *, int, int, int, int, int); static void MInsChar (struct win *, struct mchar *, int, int); static void MPutChar (struct win *, struct mchar *, int, int); static void MPutStr (struct win *, char *, int, struct mchar *, int, int); static void MWrapChar (struct win *, struct mchar *, int, int, int, int); static void MBceLine (struct win *, int, int, int, int); #define CURR_BCE (curr->w_bce ? rend_getbg(&curr->w_rend) : 0) void ResetAnsiState(struct win *p) { p->w_state = LIT; p->w_StringType = NONE; } void ResetWindow(register struct win *p) { register int i; p->w_wrap = nwin_default.wrap; p->w_origin = 0; p->w_insert = 0; p->w_revvid = 0; p->w_mouse = 0; p->w_curinv = 0; p->w_curvvis = 0; p->w_autolf = 0; p->w_keypad = 0; p->w_cursorkeys = 0; p->w_top = 0; p->w_bot = p->w_height - 1; p->w_saved.on = 0; p->w_x = p->w_y = 0; p->w_state = LIT; p->w_StringType = NONE; memset(p->w_tabs, 0, p->w_width); for (i = 8; i < p->w_width; i += 8) p->w_tabs[i] = 1; p->w_rend = mchar_null; ResetCharsets(p); p->w_bce = nwin_default.bce; } /* adds max 22 bytes */ int GetAnsiStatus(struct win *w, char *buf) { char *p = buf; if (w->w_state == LIT) return 0; strcpy(p, state_t_string[w->w_state]); p += strlen(p); if (w->w_intermediate) { *p++ = '-'; if (w->w_intermediate > 0xff) p += AddXChar(p, w->w_intermediate >> 8); p += AddXChar(p, w->w_intermediate & 0xff); *p = 0; } if (w->w_state == ASTR || w->w_state == STRESC) sprintf(p, "-%s", string_t_string[w->w_StringType]); p += strlen(p); return p - buf; } void ResetCharsets(struct win *p) { p->w_gr = nwin_default.gr; p->w_c1 = nwin_default.c1; SetCharsets(p, "BBBB02"); if (nwin_default.charset) SetCharsets(p, nwin_default.charset); ResetEncoding(p); } void SetCharsets(struct win *p, char *s) { int i; for (i = 0; i < 4 && *s; i++, s++) if (*s != '.') p->w_charsets[i] = ((*s == 'B') ? ASCII : *s); if (*s && *s++ != '.') p->w_Charset = s[-1] - '0'; if (*s && *s != '.') p->w_CharsetR = *s - '0'; p->w_ss = 0; p->w_FontL = p->w_charsets[p->w_Charset]; p->w_FontR = p->w_charsets[p->w_CharsetR]; } /*****************************************************************/ /* * Here comes the vt100 emulator * - writes logfiles, * - sets timestamp and flags activity in window. * - record program output in window scrollback * - translate program output for the display and put it into the obuf. * */ void WriteString(struct win *wp, register char *buf, register int len) { register int c; register int font; struct canvas *cv; if (!len) return; if (wp->w_log) WLogString(wp, buf, len); /* set global variables (yuck!) */ curr = wp; cols = curr->w_width; rows = curr->w_height; if (curr->w_silence) SetTimeout(&curr->w_silenceev, curr->w_silencewait * 1000); if (curr->w_monitor == MON_ON) { debug2("ACTIVITY %d %d\n", curr->w_monitor, curr->w_bell); curr->w_monitor = MON_FOUND; } if (cols > 0 && rows > 0) { do { c = (unsigned char)*buf++; if (!curr->w_mbcs) curr->w_rend.font = curr->w_FontL; /* Default: GL */ /* The next part is only for speedup */ if (curr->w_state == LIT && curr->w_encoding != UTF8 && !is_dw_font(curr->w_rend.font) && curr->w_rend.font != KANA && !curr->w_mbcs && curr->w_rend.font != '<' && c >= ' ' && c != 0x7f && ((c & 0x80) == 0 || ((c >= 0xa0 || !curr->w_c1) && !curr->w_gr)) && !curr->w_ss && !curr->w_insert && curr->w_x < cols - 1) { register int currx = curr->w_x; char *imp = buf - 1; while (currx < cols - 1) { currx++; if (--len == 0) break; c = (unsigned char)*buf++; if (c < ' ' || c == 0x7f || ((c & 0x80) && ((c < 0xa0 && curr->w_c1) || curr->w_gr))) break; } currx -= curr->w_x; if (currx > 0) { MPutStr(curr, imp, currx, &curr->w_rend, curr->w_x, curr->w_y); LPutStr(&curr->w_layer, imp, currx, &curr->w_rend, curr->w_x, curr->w_y); curr->w_x += currx; } if (len == 0) break; } /* end of speedup code */ if (curr->w_encoding == UTF8) { c = FromUtf8(c, &curr->w_decodestate); if (c == -1) continue; if (c == -2) { c = UCS_REPL; /* try char again */ buf--; len++; } if (c > 0xff) debug1("read UNICODE %04x\n", c); } tryagain: switch (curr->w_state) { case PRIN: switch (c) { case '\033': curr->w_state = PRINESC; break; default: PrintChar(c); } break; case PRINESC: switch (c) { case '[': curr->w_state = PRINCSI; break; default: PrintChar('\033'); PrintChar(c); curr->w_state = PRIN; } break; case PRINCSI: switch (c) { case '4': curr->w_state = PRIN4; break; default: PrintChar('\033'); PrintChar('['); PrintChar(c); curr->w_state = PRIN; } break; case PRIN4: switch (c) { case 'i': curr->w_state = LIT; PrintFlush(); if (curr->w_pdisplay && curr->w_pdisplay->d_printfd >= 0) { close(curr->w_pdisplay->d_printfd); curr->w_pdisplay->d_printfd = -1; } curr->w_pdisplay = 0; break; default: PrintChar('\033'); PrintChar('['); PrintChar('4'); PrintChar(c); curr->w_state = PRIN; } break; case ASTR: if (c == 0) break; if (c == '\033') { curr->w_state = STRESC; break; } /* special xterm hack: accept SetStatus sequence. Yucc! */ /* allow ^E for title escapes */ if (!(curr->w_StringType == OSC && c < ' ' && c != '\005')) if (!curr->w_c1 || c != ('\\' ^ 0xc0)) { StringChar(c); break; } c = '\\'; /* FALLTHROUGH */ case STRESC: switch (c) { case '\\': if (StringEnd() == 0 || len <= 1) break; /* check if somewhere a status is displayed */ for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext) { display = cv->c_display; if (D_status == STATUS_ON_WIN) break; } if (cv) { if (len > IOSIZE + 1) len = IOSIZE + 1; curr->w_outlen = len - 1; memmove(curr->w_outbuf, buf, len - 1); return; /* wait till status is gone */ } break; case '\033': StringChar('\033'); break; default: curr->w_state = ASTR; StringChar('\033'); StringChar(c); break; } break; case ESC: switch (c) { case '[': curr->w_NumArgs = 0; curr->w_intermediate = 0; memset((char *) curr->w_args, 0, MAXARGS * sizeof(int)); curr->w_state = CSI; break; case ']': StringStart(OSC); break; case '_': StringStart(APC); break; case 'P': StringStart(DCS); break; case '^': StringStart(PM); break; case '!': StringStart(GM); break; case '"': case 'k': StringStart(AKA); break; default: if (Special(c)) { curr->w_state = LIT; break; } debug1("not special. c = %x\n", c); if (c >= ' ' && c <= '/') { if (curr->w_intermediate) { if (curr->w_intermediate == '$') c |= '$' << 8; else c = -1; } curr->w_intermediate = c; } else if (c >= '0' && c <= '~') { DoESC(c, curr->w_intermediate); curr->w_state = LIT; } else { curr->w_state = LIT; goto tryagain; } } break; case CSI: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (curr->w_NumArgs >= 0 && curr->w_NumArgs < MAXARGS) { if (curr->w_args[curr->w_NumArgs] < 100000000) curr->w_args[curr->w_NumArgs] = 10 * curr->w_args[curr->w_NumArgs] + (c - '0'); } break; case ';': case ':': if (curr->w_NumArgs < MAXARGS) curr->w_NumArgs++; break; default: if (Special(c)) break; if (c >= '@' && c <= '~') { if (curr->w_NumArgs < MAXARGS) curr->w_NumArgs++; DoCSI(c, curr->w_intermediate); if (curr->w_state != PRIN) curr->w_state = LIT; } else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?')) curr->w_intermediate = curr->w_intermediate ? -1 : c; else { curr->w_state = LIT; goto tryagain; } } break; case LIT: default: if (curr->w_mbcs) if (c <= ' ' || c == 0x7f || (c >= 0x80 && c < 0xa0 && curr->w_c1)) curr->w_mbcs = 0; if (c < ' ') { if (c == '\033') { curr->w_intermediate = 0; curr->w_state = ESC; if (curr->w_autoaka < 0) curr->w_autoaka = 0; } else Special(c); break; } if (c >= 0x80 && c < 0xa0 && curr->w_c1) if ((curr->w_FontR & 0xf0) != 0x20 || curr->w_encoding == UTF8 ) { switch (c) { case 0xc0 ^ 'D': case 0xc0 ^ 'E': case 0xc0 ^ 'H': case 0xc0 ^ 'M': case 0xc0 ^ 'N': /* SS2 */ case 0xc0 ^ 'O': /* SS3 */ DoESC(c ^ 0xc0, 0); break; case 0xc0 ^ '[': if (curr->w_autoaka < 0) curr->w_autoaka = 0; curr->w_NumArgs = 0; curr->w_intermediate = 0; memset((char *) curr->w_args, 0, MAXARGS * sizeof(int)); curr->w_state = CSI; break; case 0xc0 ^ 'P': StringStart(DCS); break; default: break; } break; } if (!curr->w_mbcs) { if (c < 0x80 || curr->w_gr == 0) curr->w_rend.font = curr->w_FontL; else if (curr->w_gr == 2 && !curr->w_ss) curr->w_rend.font = curr->w_FontE; else curr->w_rend.font = curr->w_FontR; } if (curr->w_encoding == UTF8) { if (curr->w_rend.font == '0') { struct mchar mc, *mcp; debug1("SPECIAL %x\n", c); mc.image = c; mc.mbcs = 0; mc.font = '0'; mc.fontx = 0; mcp = recode_mchar(&mc, 0, UTF8); debug2("%02x %02x\n", mcp->image, mcp->font); c = mcp->image | mcp->font << 8; } curr->w_rend.font = 0; } if (curr->w_encoding == UTF8 && utf8_isdouble(c)) curr->w_mbcs = 0xff; if (curr->w_encoding == UTF8 && c >= 0x0300 && utf8_iscomb(c)) { int ox, oy; struct mchar omc; ox = curr->w_x - 1; oy = curr->w_y; if (ox < 0) { ox = curr->w_width - 1; oy--; } if (oy < 0) oy = 0; copy_mline2mchar(&omc, &curr->w_mlines[oy], ox); if (omc.image == 0xff && omc.font == 0xff && omc.fontx == 0) { ox--; if (ox >= 0) { copy_mline2mchar(&omc, &curr->w_mlines[oy], ox); omc.mbcs = 0xff; } } if (ox >= 0) { utf8_handle_comb(c, &omc); MFixLine(curr, oy, &omc); copy_mchar2mline(&omc, &curr->w_mlines[oy], ox); LPutChar(&curr->w_layer, &omc, ox, oy); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } break; } font = curr->w_rend.font; if (font == KANA && curr->w_encoding == SJIS && curr->w_mbcs == 0) { /* Lets see if it is the first byte of a kanji */ debug1("%x may be first of SJIS\n", c); if ((0x81 <= c && c <= 0x9f) || (0xe0 <= c && c <= 0xef)) { debug("YES!\n"); curr->w_mbcs = c; break; } } if (font == 031 && c == 0x80 && !curr->w_mbcs) font = curr->w_rend.font = 0; if (is_dw_font(font) && c == ' ') font = curr->w_rend.font = 0; if (is_dw_font(font) || curr->w_mbcs) { int t = c; if (curr->w_mbcs == 0) { curr->w_mbcs = c; break; } if (curr->w_x == cols - 1) { curr->w_x += curr->w_wrap ? 1 : -1; debug1("Patched w_x to %d\n", curr->w_x); } if (curr->w_encoding != UTF8) { c = curr->w_mbcs; if (font == KANA && curr->w_encoding == SJIS) { debug2("SJIS !! %x %x\n", c, t); /* * SJIS -> EUC mapping: * First byte: * 81,82...9f -> 21,23...5d * e0,e1...ef -> 5f,61...7d * Second byte: * 40-7e -> 21-5f * 80-9e -> 60-7e * 9f-fc -> 21-7e (increment first byte!) */ if (0x40 <= t && t <= 0xfc && t != 0x7f) { if (c <= 0x9f) c = (c - 0x81) * 2 + 0x21; else c = (c - 0xc1) * 2 + 0x21; if (t <= 0x7e) t -= 0x1f; else if (t <= 0x9e) t -= 0x20; else t -= 0x7e, c++; curr->w_rend.font = KANJI; } else { /* Incomplete shift-jis - skip first byte */ c = t; t = 0; } debug2("SJIS after %x %x\n", c, t); } if (t && curr->w_gr && font != 030 && font != 031) { t &= 0x7f; if (t < ' ') goto tryagain; } if (t == '\177') break; curr->w_mbcs = t; } } if (font == '<' && c >= ' ') { font = curr->w_rend.font = 0; c |= 0x80; } else if (curr->w_gr && curr->w_encoding != UTF8) { if (c == 0x80 && font == 0 && curr->w_encoding == GBK) c = 0xa4; else c &= 0x7f; if (c < ' ' && font != 031) goto tryagain; } if (c == '\177') break; curr->w_rend.image = c; if (curr->w_encoding == UTF8) { curr->w_rend.font = c >> 8; curr->w_rend.fontx = c >> 16; } curr->w_rend.mbcs = curr->w_mbcs; if (curr->w_x < cols - 1) { if (curr->w_insert) { save_mline(&curr->w_mlines[curr->w_y], cols); MInsChar(curr, &curr->w_rend, curr->w_x, curr->w_y); LInsChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y, &mline_old); curr->w_x++; } else { MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y); LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y); curr->w_x++; } } else if (curr->w_x == cols - 1) { MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y); LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y); if (curr->w_wrap) curr->w_x++; } else { MWrapChar(curr, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert); LWrapChar(&curr->w_layer, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert); if (curr->w_y != curr->w_bot && curr->w_y != curr->w_height - 1) curr->w_y++; curr->w_x = 1; } if (curr->w_mbcs) { curr->w_rend.mbcs = curr->w_mbcs = 0; curr->w_x++; } if (curr->w_ss) { curr->w_FontL = curr->w_charsets[curr->w_Charset]; curr->w_FontR = curr->w_charsets[curr->w_CharsetR]; curr->w_rend.font = curr->w_FontL; LSetRendition(&curr->w_layer, &curr->w_rend); curr->w_ss = 0; } break; } } while (--len); } if (!printcmd && curr->w_state == PRIN) PrintFlush(); } static void WLogString(struct win *p, char *buf, int len) { if (!p->w_log) return; if (logtstamp_on && p->w_logsilence >= logtstamp_after * 2) { char *t = MakeWinMsg(logtstamp_string, p, '%'); logfwrite(p->w_log, t, strlen(t)); /* long time no write */ } p->w_logsilence = 0; if (logfwrite(p->w_log, buf, len) < 1) { WMsg(p, errno, "Error writing logfile"); logfclose(p->w_log); p->w_log = 0; } if (!log_flush) logfflush(p->w_log); } static int Special(register int c) { switch (c) { case '\b': BackSpace(); return 1; case '\r': Return(); return 1; case '\n': if (curr->w_autoaka) FindAKA(); case '\013': /* Vertical tab is the same as Line Feed */ LineFeed(0); return 1; case '\007': WBell(curr, visual_bell); return 1; case '\t': ForwardTab(); return 1; case '\017': /* SI */ MapCharset(G0); return 1; case '\016': /* SO */ MapCharset(G1); return 1; } return 0; } static void DoESC(int c, int intermediate) { debug2("DoESC: %x - inter = %x\n", c, intermediate); switch (intermediate) { case 0: switch (c) { case 'E': LineFeed(1); break; case 'D': LineFeed(0); break; case 'M': ReverseLineFeed(); break; case 'H': curr->w_tabs[curr->w_x] = 1; break; case 'Z': /* jph: Identify as VT100 */ Report("\033[?%d;%dc", 1, 2); break; case '7': SaveCursor(&curr->w_saved); break; case '8': RestoreCursor(&curr->w_saved); break; case 'c': ClearScreen(); ResetWindow(curr); LKeypadMode(&curr->w_layer, 0); LCursorkeysMode(&curr->w_layer, 0); #ifndef TIOCPKT WNewAutoFlow(curr, 1); #endif /* XXX SetRendition(&mchar_null); InsertMode(0); ChangeScrollRegion(0, rows - 1); */ LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; case '=': LKeypadMode(&curr->w_layer, curr->w_keypad = 1); #ifndef TIOCPKT WNewAutoFlow(curr, 0); #endif /* !TIOCPKT */ break; case '>': LKeypadMode(&curr->w_layer, curr->w_keypad = 0); #ifndef TIOCPKT WNewAutoFlow(curr, 1); #endif /* !TIOCPKT */ break; case 'n': /* LS2 */ MapCharset(G2); break; case 'o': /* LS3 */ MapCharset(G3); break; case '~': MapCharsetR(G1); /* LS1R */ break; /* { */ case '}': MapCharsetR(G2); /* LS2R */ break; case '|': MapCharsetR(G3); /* LS3R */ break; case 'N': /* SS2 */ if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G2] || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G2]) curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G2]; else curr->w_ss = 0; break; case 'O': /* SS3 */ if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G3] || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G3]) curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G3]; else curr->w_ss = 0; break; case 'g': /* VBELL, private screen sequence */ WBell(curr, 1); break; } break; case '#': switch (c) { case '8': FillWithEs(); break; } break; case '(': DesignateCharset(c, G0); break; case ')': DesignateCharset(c, G1); break; case '*': DesignateCharset(c, G2); break; case '+': DesignateCharset(c, G3); break; /* * ESC $ ( Fn: invoke multi-byte charset, Fn, to G0 * ESC $ Fn: same as above. (old sequence) * ESC $ ) Fn: invoke multi-byte charset, Fn, to G1 * ESC $ * Fn: invoke multi-byte charset, Fn, to G2 * ESC $ + Fn: invoke multi-byte charset, Fn, to G3 */ case '$': case '$'<<8 | '(': DesignateCharset(c & 037, G0); break; case '$'<<8 | ')': DesignateCharset(c & 037, G1); break; case '$'<<8 | '*': DesignateCharset(c & 037, G2); break; case '$'<<8 | '+': DesignateCharset(c & 037, G3); break; } } static void DoCSI(int c, int intermediate) { register int i, a1 = curr->w_args[0], a2 = curr->w_args[1]; if (curr->w_NumArgs > MAXARGS) curr->w_NumArgs = MAXARGS; switch (intermediate) { case 0: switch (c) { case 'H': case 'f': if (a1 < 1) a1 = 1; if (curr->w_origin) a1 += curr->w_top; if (a1 > rows) a1 = rows; if (a2 < 1) a2 = 1; if (a2 > cols) a2 = cols; LGotoPos(&curr->w_layer, --a2, --a1); curr->w_x = a2; curr->w_y = a1; if (curr->w_autoaka) curr->w_autoaka = a1 + 1; break; case 'J': if (a1 < 0 || a1 > 2) a1 = 0; switch (a1) { case 0: ClearToEOS(); break; case 1: ClearFromBOS(); break; case 2: ClearScreen(); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; } break; case 'K': if (a1 < 0 || a1 > 2) a1 %= 3; switch (a1) { case 0: ClearLineRegion(curr->w_x, cols - 1); break; case 1: ClearLineRegion(0, curr->w_x); break; case 2: ClearLineRegion(0, cols - 1); break; } break; case 'X': a1 = curr->w_x + (a1 ? a1 - 1 : 0); ClearLineRegion(curr->w_x, a1 < cols ? a1 : cols - 1); break; case 'A': CursorUp(a1 ? a1 : 1); break; case 'B': CursorDown(a1 ? a1 : 1); break; case 'C': CursorRight(a1 ? a1 : 1); break; case 'D': CursorLeft(a1 ? a1 : 1); break; case 'E': curr->w_x = 0; CursorDown(a1 ? a1 : 1); /* positions cursor */ break; case 'F': curr->w_x = 0; CursorUp(a1 ? a1 : 1); /* positions cursor */ break; case 'G': case '`': /* HPA */ curr->w_x = a1 ? a1 - 1 : 0; if (curr->w_x >= cols) curr->w_x = cols - 1; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; case 'd': /* VPA */ curr->w_y = a1 ? a1 - 1 : 0; if (curr->w_y >= rows) curr->w_y = rows - 1; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; case 'm': SelectRendition(); break; case 'g': if (a1 == 0) curr->w_tabs[curr->w_x] = 0; else if (a1 == 3) memset(curr->w_tabs, 0, cols); break; case 'r': if (!a1) a1 = 1; if (!a2) a2 = rows; if (a1 < 1 || a2 > rows || a1 >= a2) break; curr->w_top = a1 - 1; curr->w_bot = a2 - 1; /* ChangeScrollRegion(curr->w_top, curr->w_bot); */ if (curr->w_origin) { curr->w_y = curr->w_top; curr->w_x = 0; } else curr->w_y = curr->w_x = 0; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; case 's': SaveCursor(&curr->w_saved); break; case 't': switch(a1) { case 11: if (curr->w_layer.l_cvlist) Report("\033[1t", 0, 0); else Report("\033[2t", 0, 0); break; case 7: LRefreshAll(&curr->w_layer, 0); break; case 21: a1 = strlen(curr->w_title); if ((unsigned)(curr->w_inlen + 5 + a1) <= sizeof(curr->w_inbuf)) { memmove(curr->w_inbuf + curr->w_inlen, "\033]l", 3); memmove(curr->w_inbuf + curr->w_inlen + 3, curr->w_title, a1); memmove(curr->w_inbuf + curr->w_inlen + 3 + a1, "\033\\", 2); curr->w_inlen += 5 + a1; } break; case 8: a1 = curr->w_args[2]; if (a1 < 1) a1 = curr->w_width; if (a2 < 1) a2 = curr->w_height; if (a1 > 10000 || a2 > 10000) break; WChangeSize(curr, a1, a2); cols = curr->w_width; rows = curr->w_height; break; default: break; } break; case 'u': RestoreCursor(&curr->w_saved); break; case 'I': if (!a1) a1 = 1; while (a1--) ForwardTab(); break; case 'Z': if (!a1) a1 = 1; while (a1--) BackwardTab(); break; case 'L': InsertLine(a1 ? a1 : 1); break; case 'M': DeleteLine(a1 ? a1 : 1); break; case 'P': DeleteChar(a1 ? a1 : 1); break; case '@': InsertChar(a1 ? a1 : 1); break; case 'h': ASetMode(1); break; case 'l': ASetMode(0); break; case 'i': /* MC Media Control */ if (a1 == 5) PrintStart(); break; case 'n': if (a1 == 5) /* Report terminal status */ Report("\033[0n", 0, 0); else if (a1 == 6) /* Report cursor position */ Report("\033[%d;%dR", curr->w_y + 1, curr->w_x + 1); break; case 'c': /* Identify as VT100 */ if (a1 == 0) Report("\033[?%d;%dc", 1, 2); break; case 'x': /* decreqtparm */ if (a1 == 0 || a1 == 1) Report("\033[%d;1;1;112;112;1;0x", a1 + 2, 0); break; case 'p': /* obscure code from a 97801 term */ if (a1 == 6 || a1 == 7) { curr->w_curinv = 7 - a1; LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); } break; case 'S': /* code from a 97801 term / DEC vt400 */ ScrollRegion(a1 ? a1 : 1); break; case 'T': /* code from a 97801 term / DEC vt400 */ case '^': /* SD as per ISO 6429 */ ScrollRegion(a1 ? -a1 : -1); break; } break; case '?': for (a2 = 0; a2 < curr->w_NumArgs; a2++) { a1 = curr->w_args[a2]; debug2("\\E[?%d%c\n",a1,c); if (c != 'h' && c != 'l') break; i = (c == 'h'); switch (a1) { case 1: /* CKM: cursor key mode */ LCursorkeysMode(&curr->w_layer, curr->w_cursorkeys = i); #ifndef TIOCPKT WNewAutoFlow(curr, !i); #endif /* !TIOCPKT */ break; case 2: /* ANM: ansi/vt52 mode */ if (i) { if (curr->w_encoding) break; curr->w_charsets[0] = curr->w_charsets[1] = curr->w_charsets[2] = curr->w_charsets[3] = curr->w_FontL = curr->w_FontR = ASCII; curr->w_Charset = 0; curr->w_CharsetR = 2; curr->w_ss = 0; } break; case 3: /* COLM: column mode */ i = (i ? Z0width : Z1width); ClearScreen(); curr->w_x = 0; curr->w_y = 0; WChangeSize(curr, i, curr->w_height); cols = curr->w_width; rows = curr->w_height; break; /* case 4: SCLM: scrolling mode */ case 5: /* SCNM: screen mode */ if (i != curr->w_revvid) WReverseVideo(curr, i); curr->w_revvid = i; break; case 6: /* OM: origin mode */ if ((curr->w_origin = i) != 0) { curr->w_y = curr->w_top; curr->w_x = 0; } else curr->w_y = curr->w_x = 0; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); break; case 7: /* AWM: auto wrap mode */ curr->w_wrap = i; break; /* case 8: ARM: auto repeat mode */ /* case 9: INLM: interlace mode */ case 9: /* X10 mouse tracking */ curr->w_mouse = i ? 9 : 0; LMouseMode(&curr->w_layer, curr->w_mouse); break; /* case 10: EDM: edit mode */ /* case 11: LTM: line transmit mode */ /* case 13: SCFDM: space compression / field delimiting */ /* case 14: TEM: transmit execution mode */ /* case 16: EKEM: edit key execution mode */ /* case 18: PFF: Printer term form feed */ /* case 19: PEX: Printer extend screen / scroll. reg */ case 25: /* TCEM: text cursor enable mode */ curr->w_curinv = !i; LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); break; /* case 34: RLM: Right to left mode */ /* case 35: HEBM: hebrew keyboard map */ /* case 36: HEM: hebrew encoding */ /* case 38: TeK Mode */ /* case 40: 132 col enable */ /* case 42: NRCM: 7bit NRC character mode */ /* case 44: margin bell enable */ case 47: /* xterm-like alternate screen */ case 1047: /* xterm-like alternate screen */ case 1049: /* xterm-like alternate screen */ if (use_altscreen) { if (i) { if (!curr->w_alt.on) { SaveCursor(&curr->w_alt.cursor); EnterAltScreen(curr); } } else { if (curr->w_alt.on) { RestoreCursor(&curr->w_alt.cursor); LeaveAltScreen(curr); } } if (a1 == 47 && !i) curr->w_saved.on = 0; LRefreshAll(&curr->w_layer, 0); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } break; case 1048: if (i) SaveCursor(&curr->w_saved); else RestoreCursor(&curr->w_saved); break; /* case 66: NKM: Numeric keypad appl mode */ /* case 68: KBUM: Keyboard usage mode (data process) */ case 1000: /* VT200 mouse tracking */ case 1001: /* VT200 highlight mouse */ case 1002: /* button event mouse*/ case 1003: /* any event mouse*/ curr->w_mouse = i ? a1 : 0; LMouseMode(&curr->w_layer, curr->w_mouse); break; } } break; case '>': switch (c) { case 'c': /* secondary DA */ if (a1 == 0) Report("\033[>%d;%d;0c", 83, nversion); /* 83 == 'S' */ break; } break; } } static void StringStart(enum string_t type) { curr->w_StringType = type; curr->w_stringp = curr->w_string; curr->w_state = ASTR; } static void StringChar(int c) { if (curr->w_stringp >= curr->w_string + MAXSTR - 1) curr->w_state = LIT; else *(curr->w_stringp)++ = c; } /* * Do string processing. Returns -1 if output should be suspended * until status is gone. */ static int StringEnd() { struct canvas *cv; char *p; int typ; curr->w_state = LIT; *curr->w_stringp = '\0'; switch (curr->w_StringType) { case OSC: /* special xterm compatibility hack */ if (curr->w_string[0] == ';' || (p = strchr(curr->w_string, ';')) == 0) break; typ = atoi(curr->w_string); p++; if (typ == 83) /* 83 = 'S' */ { /* special execute commands sequence */ char *args[MAXARGS]; int argl[MAXARGS]; struct acluser *windowuser; windowuser = *FindUserPtr(":window:"); if (windowuser && Parse(p, sizeof(curr->w_string) - (p - curr->w_string), args, argl)) { for (display = displays; display; display = display->d_next) if (D_forecv->c_layer->l_bottom == &curr->w_layer) break; /* found it */ if (display == 0 && curr->w_layer.l_cvlist) display = curr->w_layer.l_cvlist->c_display; if (display == 0) display = displays; EffectiveAclUser = windowuser; fore = curr; flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer; DoCommand(args, argl); EffectiveAclUser = 0; fore = 0; flayer = 0; } break; } if (typ == 0 || typ == 1 || typ == 2 || typ == 20 || typ == 39 || typ == 49) { int typ2; typ2 = typ / 10; if (--typ2 < 0) typ2 = 0; if (strcmp(curr->w_xtermosc[typ2], p)) { strncpy(curr->w_xtermosc[typ2], p, sizeof(curr->w_xtermosc[typ2]) - 1); curr->w_xtermosc[typ2][sizeof(curr->w_xtermosc[typ2]) - 1] = 0; for (display = displays; display; display = display->d_next) { if (!D_CXT) continue; if (D_forecv->c_layer->l_bottom == &curr->w_layer) SetXtermOSC(typ2, curr->w_xtermosc[typ2]); if ((typ2 == 2 || typ2 == 3) && D_xtermosc[typ2]) Redisplay(0); } } } if (typ != 0 && typ != 2) break; curr->w_stringp -= p - curr->w_string; if (curr->w_stringp > curr->w_string) memmove(curr->w_string, p, curr->w_stringp - curr->w_string); *curr->w_stringp = '\0'; /* FALLTHROUGH */ case APC: if (curr->w_hstatus) { if (strcmp(curr->w_hstatus, curr->w_string) == 0) break; /* not changed */ free(curr->w_hstatus); curr->w_hstatus = 0; } if (curr->w_string != curr->w_stringp) curr->w_hstatus = SaveStr(curr->w_string); WindowChanged(curr, 'h'); break; case PM: case GM: for (display = displays; display; display = display->d_next) { for (cv = D_cvlist; cv; cv = cv->c_next) if (cv->c_layer->l_bottom == &curr->w_layer) break; if (cv || curr->w_StringType == GM) MakeStatus(curr->w_string); } return -1; case DCS: LAY_DISPLAYS(&curr->w_layer, AddStr(curr->w_string)); break; case AKA: if (curr->w_title == curr->w_akabuf && !*curr->w_string) break; ChangeAKA(curr, curr->w_string, strlen(curr->w_string)); if (!*curr->w_string) curr->w_autoaka = curr->w_y + 1; break; default: break; } return 0; } static void PrintStart() { curr->w_pdisplay = 0; /* find us a nice display to print on, fore prefered */ display = curr->w_lastdisp; if (!(display && curr == D_fore && (printcmd || D_PO))) for (display = displays; display; display = display->d_next) if (curr == D_fore && (printcmd || D_PO)) break; if (!display) { struct canvas *cv; for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext) { display = cv->c_display; if (printcmd || D_PO) break; } if (!cv) { display = displays; if (!display || display->d_next || !(printcmd || D_PO)) return; } } curr->w_pdisplay = display; curr->w_stringp = curr->w_string; curr->w_state = PRIN; if (printcmd && curr->w_pdisplay->d_printfd < 0) curr->w_pdisplay->d_printfd = printpipe(curr, printcmd); } static void PrintChar(int c) { if (curr->w_stringp >= curr->w_string + MAXSTR - 1) PrintFlush(); *(curr->w_stringp)++ = c; } static void PrintFlush() { display = curr->w_pdisplay; if (display && printcmd) { char *bp = curr->w_string; int len = curr->w_stringp - curr->w_string; int r; while (len && display->d_printfd >= 0) { r = write(display->d_printfd, bp, len); if (r <= 0) { WMsg(curr, errno, "printing aborted"); close(display->d_printfd); display->d_printfd = -1; break; } bp += r; len -= r; } } else if (display && curr->w_stringp > curr->w_string) { AddCStr(D_PO); AddStrn(curr->w_string, curr->w_stringp - curr->w_string); AddCStr(D_PF); Flush(3); } curr->w_stringp = curr->w_string; } void WNewAutoFlow(struct win *win, int on) { debug1("WNewAutoFlow: %d\n", on); if (win->w_flow & FLOW_AUTOFLAG) win->w_flow = FLOW_AUTOFLAG | (FLOW_AUTO|FLOW_NOW) * on; else win->w_flow = (win->w_flow & ~FLOW_AUTO) | FLOW_AUTO * on; LSetFlow(&win->w_layer, win->w_flow & FLOW_NOW); } static void DesignateCharset(int c, int n) { curr->w_ss = 0; if (c == ('@' & 037)) /* map JIS 6226 to 0208 */ c = KANJI; if (c == 'B') c = ASCII; if (curr->w_charsets[n] != c) { curr->w_charsets[n] = c; if (curr->w_Charset == n) { curr->w_FontL = c; curr->w_rend.font = curr->w_FontL; LSetRendition(&curr->w_layer, &curr->w_rend); } if (curr->w_CharsetR == n) curr->w_FontR = c; } } static void MapCharset(int n) { curr->w_ss = 0; if (curr->w_Charset != n) { curr->w_Charset = n; curr->w_FontL = curr->w_charsets[n]; curr->w_rend.font = curr->w_FontL; LSetRendition(&curr->w_layer, &curr->w_rend); } } static void MapCharsetR(int n) { curr->w_ss = 0; if (curr->w_CharsetR != n) { curr->w_CharsetR = n; curr->w_FontR = curr->w_charsets[n]; } curr->w_gr = 1; } static void SaveCursor(struct cursor *cursor) { cursor->on = 1; cursor->x = curr->w_x; cursor->y = curr->w_y; cursor->Rend = curr->w_rend; cursor->Charset = curr->w_Charset; cursor->CharsetR = curr->w_CharsetR; memmove((char *) cursor->Charsets, (char *) curr->w_charsets, 4 * sizeof(int)); } static void RestoreCursor(struct cursor *cursor) { if (!cursor->on) return; LGotoPos(&curr->w_layer, cursor->x, cursor->y); curr->w_x = cursor->x; curr->w_y = cursor->y; curr->w_rend = cursor->Rend; memmove((char *) curr->w_charsets, (char *) cursor->Charsets, 4 * sizeof(int)); curr->w_Charset = cursor->Charset; curr->w_CharsetR = cursor->CharsetR; curr->w_ss = 0; curr->w_FontL = curr->w_charsets[curr->w_Charset]; curr->w_FontR = curr->w_charsets[curr->w_CharsetR]; LSetRendition(&curr->w_layer, &curr->w_rend); } static void BackSpace() { if (curr->w_x > 0) { curr->w_x--; } else if (curr->w_wrap && curr->w_y > 0) { curr->w_x = cols - 1; curr->w_y--; } LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void Return() { if (curr->w_x == 0) return; curr->w_x = 0; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void LineFeed(int out_mode) { /* out_mode: 0=lf, 1=cr+lf */ if (out_mode) curr->w_x = 0; if (curr->w_y != curr->w_bot) /* Don't scroll */ { if (curr->w_y < rows-1) curr->w_y++; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); return; } if (curr->w_autoaka > 1) curr->w_autoaka--; MScrollV(curr, 1, curr->w_top, curr->w_bot, CURR_BCE); LScrollV(&curr->w_layer, 1, curr->w_top, curr->w_bot, CURR_BCE); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void ReverseLineFeed() { if (curr->w_y == curr->w_top) { MScrollV(curr, -1, curr->w_top, curr->w_bot, CURR_BCE); LScrollV(&curr->w_layer, -1, curr->w_top, curr->w_bot, CURR_BCE); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } else if (curr->w_y > 0) CursorUp(1); } static void InsertChar(int n) { register int y = curr->w_y, x = curr->w_x; if (n <= 0) return; if (x == cols) x--; save_mline(&curr->w_mlines[y], cols); MScrollH(curr, -n, y, x, curr->w_width - 1, CURR_BCE); LScrollH(&curr->w_layer, -n, y, x, curr->w_width - 1, CURR_BCE, &mline_old); LGotoPos(&curr->w_layer, x, y); } static void DeleteChar(int n) { register int y = curr->w_y, x = curr->w_x; if (x == cols) x--; save_mline(&curr->w_mlines[y], cols); MScrollH(curr, n, y, x, curr->w_width - 1, CURR_BCE); LScrollH(&curr->w_layer, n, y, x, curr->w_width - 1, CURR_BCE, &mline_old); LGotoPos(&curr->w_layer, x, y); } static void DeleteLine(int n) { if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot) return; if (n > curr->w_bot - curr->w_y + 1) n = curr->w_bot - curr->w_y + 1; MScrollV(curr, n, curr->w_y, curr->w_bot, CURR_BCE); LScrollV(&curr->w_layer, n, curr->w_y, curr->w_bot, CURR_BCE); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void InsertLine(int n) { if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot) return; if (n > curr->w_bot - curr->w_y + 1) n = curr->w_bot - curr->w_y + 1; MScrollV(curr, -n, curr->w_y, curr->w_bot, CURR_BCE); LScrollV(&curr->w_layer, -n, curr->w_y, curr->w_bot, CURR_BCE); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void ScrollRegion(int n) { MScrollV(curr, n, curr->w_top, curr->w_bot, CURR_BCE); LScrollV(&curr->w_layer, n, curr->w_top, curr->w_bot, CURR_BCE); LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void ForwardTab() { register int x = curr->w_x; if (x == cols) { LineFeed(1); x = 0; } if (curr->w_tabs[x] && x < cols - 1) x++; while (x < cols - 1 && !curr->w_tabs[x]) x++; curr->w_x = x; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void BackwardTab() { register int x = curr->w_x; if (curr->w_tabs[x] && x > 0) x--; while (x > 0 && !curr->w_tabs[x]) x--; curr->w_x = x; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void ClearScreen() { LClearArea(&curr->w_layer, 0, 0, curr->w_width - 1, curr->w_height - 1, CURR_BCE, 1); MScrollV(curr, curr->w_height, 0, curr->w_height - 1, CURR_BCE); } static void ClearFromBOS() { register int y = curr->w_y, x = curr->w_x; LClearArea(&curr->w_layer, 0, 0, x, y, CURR_BCE, 1); MClearArea(curr, 0, 0, x, y, CURR_BCE); RestorePosRendition(); } static void ClearToEOS() { register int y = curr->w_y, x = curr->w_x; if (x == 0 && y == 0) { ClearScreen(); RestorePosRendition(); return; } LClearArea(&curr->w_layer, x, y, cols - 1, rows - 1, CURR_BCE, 1); MClearArea(curr, x, y, cols - 1, rows - 1, CURR_BCE); RestorePosRendition(); } static void ClearLineRegion(int from, int to) { register int y = curr->w_y; LClearArea(&curr->w_layer, from, y, to, y, CURR_BCE, 1); MClearArea(curr, from, y, to, y, CURR_BCE); RestorePosRendition(); } static void CursorRight(register int n) { register int x = curr->w_x; if (x == cols) { LineFeed(1); x = 0; } if ((curr->w_x += n) >= cols) curr->w_x = cols - 1; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void CursorUp(register int n) { if (curr->w_y < curr->w_top) /* if above scrolling rgn, */ { if ((curr->w_y -= n) < 0) /* ignore its limits */ curr->w_y = 0; } else if ((curr->w_y -= n) < curr->w_top) curr->w_y = curr->w_top; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void CursorDown(register int n) { if (curr->w_y > curr->w_bot) /* if below scrolling rgn, */ { if ((curr->w_y += n) > rows - 1) /* ignore its limits */ curr->w_y = rows - 1; } else if ((curr->w_y += n) > curr->w_bot) curr->w_y = curr->w_bot; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void CursorLeft(register int n) { if ((curr->w_x -= n) < 0) curr->w_x = 0; LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); } static void ASetMode(int on) { register int i; for (i = 0; i < curr->w_NumArgs; ++i) { switch (curr->w_args[i]) { /* case 2: KAM: Lock keyboard */ case 4: /* IRM: Insert mode */ curr->w_insert = on; LAY_DISPLAYS(&curr->w_layer, InsertMode(on)); break; /* case 12: SRM: Echo mode on */ case 20: /* LNM: Linefeed mode */ curr->w_autolf = on; break; case 34: curr->w_curvvis = !on; LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis); break; default: break; } } } static char rendlist[] = { ~((1 << NATTR) - 1), A_BD, A_DI, A_SO, A_US, A_BL, 0, A_RV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ~(A_BD|A_SO|A_DI), ~A_SO, ~A_US, ~A_BL, 0, ~A_RV }; static void SelectRendition() { register int j, i = 0, a = curr->w_rend.attr, c = curr->w_rend.color; int cx = curr->w_rend.colorx; do { j = curr->w_args[i]; if ((j == 38 || j == 48) && i + 2 < curr->w_NumArgs && curr->w_args[i + 1] == 5) { int jj; i += 2; jj = curr->w_args[i]; if (jj < 0 || jj > 255) continue; if (j == 38) { c = (c & 0xf0) | ((jj & 0x0f) ^ 9); a |= A_BFG; if (jj >= 8 && jj < 16) c |= 0x08; else a ^= A_BFG; a = (a & 0xbf) | (jj & 8 ? 0x40 : 0); cx = (cx & 0xf0) | (jj >> 4 & 0x0f); } else { c = (c & 0x0f) | ((jj & 0x0f) ^ 9) << 4; a |= A_BBG; if (jj >= 8 && jj < 16) c |= 0x80; else a ^= A_BBG; cx = (cx & 0x0f) | (jj & 0xf0); } continue; } if (j == 0 || (j >= 30 && j <= 39 && j != 38)) a &= 0xbf; if (j == 0 || (j >= 40 && j <= 49 && j != 48)) a &= 0x7f; if (j >= 90 && j <= 97) a |= 0x40; if (j >= 100 && j <= 107) a |= 0x80; if (j >= 90 && j <= 97) j -= 60; if (j >= 100 && j <= 107) j -= 60; if (j >= 30 && j <= 39 && j != 38) c = (c & 0xf0) | ((j - 30) ^ 9); else if (j >= 40 && j <= 49 && j != 48) c = (c & 0x0f) | (((j - 40) ^ 9) << 4); if (j == 0) c = 0; if (j == 0 || (j >= 30 && j <= 39 && j != 38)) cx &= 0xf0; if (j == 0 || (j >= 40 && j <= 49 && j != 48)) cx &= 0x0f; if (j < 0 || j >= (int)(sizeof(rendlist)/sizeof(*rendlist))) continue; j = rendlist[j]; if (j & (1 << NATTR)) a &= j; else a |= j; } while (++i < curr->w_NumArgs); curr->w_rend.attr = a; curr->w_rend.color = c; curr->w_rend.colorx = cx; LSetRendition(&curr->w_layer, &curr->w_rend); } static void FillWithEs() { register int i; register unsigned char *p, *ep; LClearAll(&curr->w_layer, 1); curr->w_y = curr->w_x = 0; for (i = 0; i < rows; ++i) { clear_mline(&curr->w_mlines[i], 0, cols + 1); p = curr->w_mlines[i].image; ep = p + cols; while (p < ep) *p++ = 'E'; } LRefreshAll(&curr->w_layer, 1); } /* * Ugly autoaka hack support: * ChangeAKA() sets a new aka * FindAKA() searches for an autoaka match */ void ChangeAKA(struct win *p, char *s, int l) { int i, c; for (i = 0; l > 0; l--) { if (p->w_akachange + i == p->w_akabuf + sizeof(p->w_akabuf) - 1) break; c = (unsigned char)*s++; if (c == 0) break; if (c < 32 || c == 127 || (c >= 128 && c < 160 && p->w_c1)) continue; p->w_akachange[i++] = c; } p->w_akachange[i] = 0; p->w_title = p->w_akachange; if (p->w_akachange != p->w_akabuf) if (p->w_akachange[0] == 0 || p->w_akachange[-1] == ':') p->w_title = p->w_akabuf + strlen(p->w_akabuf) + 1; WindowChanged(p, 't'); WindowChanged((struct win *)0, 'w'); WindowChanged((struct win *)0, 'W'); } static void FindAKA() { register unsigned char *cp, *line; register struct win *wp = curr; register int len = strlen(wp->w_akabuf); int y; y = (wp->w_autoaka > 0 && wp->w_autoaka <= wp->w_height) ? wp->w_autoaka - 1 : wp->w_y; cols = wp->w_width; try_line: cp = line = wp->w_mlines[y].image; if (wp->w_autoaka > 0 && *wp->w_akabuf != '\0') { for (;;) { if (cp - line >= cols - len) { if (++y == wp->w_autoaka && y < rows) goto try_line; return; } if (strncmp((char *)cp, wp->w_akabuf, len) == 0) break; cp++; } cp += len; } for (len = cols - (cp - line); len && *cp == ' '; len--, cp++) ; if (len) { if (wp->w_autoaka > 0 && (*cp == '!' || *cp == '%' || *cp == '^')) wp->w_autoaka = -1; else wp->w_autoaka = 0; line = cp; while (len && *cp != ' ') { if (*cp++ == '/') line = cp; len--; } ChangeAKA(wp, (char *)line, cp - line); } else wp->w_autoaka = 0; } static void RestorePosRendition() { LGotoPos(&curr->w_layer, curr->w_x, curr->w_y); LSetRendition(&curr->w_layer, &curr->w_rend); } /* Send a terminal report as if it were typed. */ static void Report(char *fmt, int n1, int n2) { register int len; char rbuf[40]; /* enough room for all replys */ sprintf(rbuf, fmt, n1, n2); len = strlen(rbuf); if (W_UWP(curr)) { if ((unsigned)(curr->w_pwin->p_inlen + len) <= sizeof(curr->w_pwin->p_inbuf)) { memmove(curr->w_pwin->p_inbuf + curr->w_pwin->p_inlen, rbuf, len); curr->w_pwin->p_inlen += len; } } else { if ((unsigned)(curr->w_inlen + len) <= sizeof(curr->w_inbuf)) { memmove(curr->w_inbuf + curr->w_inlen, rbuf, len); curr->w_inlen += len; } } } /* *====================================================================* *====================================================================* */ /********************************************************************** * * Memory subsystem. * */ static void MFixLine(struct win *p, int y, struct mchar *mc) { struct mline *ml = &p->w_mlines[y]; if (mc->attr && ml->attr == null) { if ((ml->attr = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) { ml->attr = null; mc->attr = p->w_rend.attr = 0; WMsg(p, 0, "Warning: no space for attr - turned off"); } } if (mc->font && ml->font == null) { if ((ml->font = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) { ml->font = null; p->w_FontL = p->w_charsets[p->w_ss ? p->w_ss : p->w_Charset] = 0; p->w_FontR = p->w_charsets[p->w_ss ? p->w_ss : p->w_CharsetR] = 0; mc->font = mc->fontx = p->w_rend.font = 0; WMsg(p, 0, "Warning: no space for font - turned off"); } } if (mc->fontx && ml->fontx == null) { if ((ml->fontx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) { ml->fontx = null; mc->fontx = 0; } } if (mc->color && ml->color == null) { if ((ml->color = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) { ml->color = null; mc->color = p->w_rend.color = 0; WMsg(p, 0, "Warning: no space for color - turned off"); } } if (mc->colorx && ml->colorx == null) { if ((ml->colorx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0) { ml->colorx = null; mc->colorx = p->w_rend.colorx = 0; WMsg(p, 0, "Warning: no space for extended colors - turned off"); } } } /*****************************************************************/ # define MKillDwRight(p, ml, x) \ if (dw_right(ml, x, p->w_encoding)) \ { \ if (x > 0) \ copy_mchar2mline(&mchar_blank, ml, x - 1); \ copy_mchar2mline(&mchar_blank, ml, x); \ } # define MKillDwLeft(p, ml, x) \ if (dw_left(ml, x, p->w_encoding)) \ { \ copy_mchar2mline(&mchar_blank, ml, x); \ copy_mchar2mline(&mchar_blank, ml, x + 1); \ } static void MScrollH(struct win *p, int n, int y, int xs, int xe, int bce) { struct mline *ml; if (n == 0) return; ml = &p->w_mlines[y]; MKillDwRight(p, ml, xs); MKillDwLeft(p, ml, xe); if (n > 0) { if (xe - xs + 1 > n) { MKillDwRight(p, ml, xs + n); bcopy_mline(ml, xs + n, xs, xe + 1 - xs - n); } else n = xe - xs + 1; clear_mline(ml, xe + 1 - n, n); if (bce) MBceLine(p, y, xe + 1 - n, n, bce); } else { n = -n; if (xe - xs + 1 > n) { MKillDwLeft(p, ml, xe - n); bcopy_mline(ml, xs, xs + n, xe + 1 - xs - n); } else n = xe - xs + 1; clear_mline(ml, xs, n); if (bce) MBceLine(p, y, xs, n, bce); } } static void MScrollV(struct win *p, int n, int ys, int ye, int bce) { int i, cnt1, cnt2; struct mline tmp[256]; struct mline *ml; if (n == 0) return; if (n > 0) { if (n > 256) { MScrollV(p, n - 256, ys, ye, bce); n = 256; } if (ye - ys + 1 < n) n = ye - ys + 1; if (compacthist) { ye = MFindUsedLine(p, ye, ys); if (ye - ys + 1 < n) n = ye - ys + 1; if (n <= 0) return; } /* Clear lines */ ml = p->w_mlines + ys; for (i = ys; i < ys + n; i++, ml++) { if (ys == p->w_top) WAddLineToHist(p, ml); if (ml->attr != null) free(ml->attr); ml->attr = null; if (ml->font != null) free(ml->font); ml->font = null; if (ml->fontx != null) free(ml->fontx); ml->fontx = null; if (ml->color != null) free(ml->color); ml->color = null; if (ml->colorx != null) free(ml->colorx); ml->colorx = null; bclear((char *)ml->image, p->w_width + 1); if (bce) MBceLine(p, i, 0, p->w_width, bce); } /* switch 'em over */ cnt1 = n * sizeof(struct mline); cnt2 = (ye - ys + 1 - n) * sizeof(struct mline); if (cnt1 && cnt2) Scroll((char *)(p->w_mlines + ys), cnt1, cnt2, (char *)tmp); } else { if (n < -256) { MScrollV(p, n + 256, ys, ye, bce); n = -256; } n = -n; if (ye - ys + 1 < n) n = ye - ys + 1; ml = p->w_mlines + ye; /* Clear lines */ for (i = ye; i > ye - n; i--, ml--) { if (ml->attr != null) free(ml->attr); ml->attr = null; if (ml->font != null) free(ml->font); ml->font = null; if (ml->fontx != null) free(ml->fontx); ml->fontx = null; if (ml->color != null) free(ml->color); ml->color = null; if (ml->colorx != null) free(ml->colorx); ml->colorx = null; bclear((char *)ml->image, p->w_width + 1); if (bce) MBceLine(p, i, 0, p->w_width, bce); } cnt1 = n * sizeof(struct mline); cnt2 = (ye - ys + 1 - n) * sizeof(struct mline); if (cnt1 && cnt2) Scroll((char *)(p->w_mlines + ys), cnt2, cnt1, (char *)tmp); } } static void Scroll(char *cp, int cnt1, int cnt2, char *tmp) { if (!cnt1 || !cnt2) return; if (cnt1 <= cnt2) { memmove(tmp, cp, cnt1); memmove(cp, cp + cnt1, cnt2); memmove(cp + cnt2, tmp, cnt1); } else { memmove(tmp, cp + cnt1, cnt2); memmove(cp + cnt2, cp, cnt1); memmove(cp, tmp, cnt2); } } static void MClearArea(struct win *p, int xs, int ys, int xe, int ye, int bce) { int n, y; int xxe; struct mline *ml; /* Check for zero-height window */ if (ys < 0 || ye < ys) return; /* check for magic margin condition */ if (xs >= p->w_width) xs = p->w_width - 1; if (xe >= p->w_width) xe = p->w_width - 1; MKillDwRight(p, p->w_mlines + ys, xs); MKillDwLeft(p, p->w_mlines + ye, xe); ml = p->w_mlines + ys; for (y = ys; y <= ye; y++, ml++) { xxe = (y == ye) ? xe : p->w_width - 1; n = xxe - xs + 1; if (n > 0) clear_mline(ml, xs, n); if (n > 0 && bce) MBceLine(p, y, xs, xs + n - 1, bce); xs = 0; } } static void MInsChar(struct win *p, struct mchar *c, int x, int y) { int n; struct mline *ml; ASSERT(x >= 0 && x < p->w_width); MFixLine(p, y, c); ml = p->w_mlines + y; n = p->w_width - x - 1; MKillDwRight(p, ml, x); if (n > 0) { MKillDwRight(p, ml, p->w_width - 1); bcopy_mline(ml, x, x + 1, n); } copy_mchar2mline(c, ml, x); if (c->mbcs) { if (--n > 0) { MKillDwRight(p, ml, p->w_width - 1); bcopy_mline(ml, x + 1, x + 2, n); } copy_mchar2mline(c, ml, x + 1); ml->image[x + 1] = c->mbcs; if (p->w_encoding != UTF8) ml->font[x + 1] |= 0x80; else if (p->w_encoding == UTF8 && c->mbcs) { ml->font[x + 1] = c->mbcs; ml->fontx[x + 1] = 0; } } } static void MPutChar(struct win *p, struct mchar *c, int x, int y) { struct mline *ml; MFixLine(p, y, c); ml = &p->w_mlines[y]; MKillDwRight(p, ml, x); MKillDwLeft(p, ml, x); copy_mchar2mline(c, ml, x); if (c->mbcs) { MKillDwLeft(p, ml, x + 1); copy_mchar2mline(c, ml, x + 1); ml->image[x + 1] = c->mbcs; if (p->w_encoding != UTF8) ml->font[x + 1] |= 0x80; else if (p->w_encoding == UTF8 && c->mbcs) { ml->font[x + 1] = c->mbcs; ml->fontx[x + 1] = 0; } } } static void MWrapChar(struct win *p, struct mchar *c, int y, int top, int bot, int ins) { struct mline *ml; int bce; bce = rend_getbg(c); MFixLine(p, y, c); ml = &p->w_mlines[y]; copy_mchar2mline(&mchar_null, ml, p->w_width); if (y == bot) MScrollV(p, 1, top, bot, bce); else if (y < p->w_height - 1) y++; if (ins) MInsChar(p, c, 0, y); else MPutChar(p, c, 0, y); } static void MPutStr(struct win *p, char *s, int n, struct mchar *r, int x, int y) { struct mline *ml; int i; unsigned char *b; if (n <= 0) return; MFixLine(p, y, r); ml = &p->w_mlines[y]; MKillDwRight(p, ml, x); MKillDwLeft(p, ml, x + n - 1); memmove((char *)ml->image + x, s, n); if (ml->attr != null) { b = ml->attr + x; for (i = n; i-- > 0;) *b++ = r->attr; } if (ml->font != null) { b = ml->font + x; for (i = n; i-- > 0;) *b++ = r->font; } if (ml->fontx != null) { b = ml->fontx + x; for (i = n; i-- > 0;) *b++ = r->fontx; } if (ml->color != null) { b = ml->color + x; for (i = n; i-- > 0;) *b++ = r->color; } if (ml->colorx != null) { b = ml->colorx + x; for (i = n; i-- > 0;) *b++ = r->colorx; } } static void MBceLine(struct win *p, int y, int xs, int xe, int bce) { struct mchar mc; struct mline *ml; int x; mc = mchar_null; rend_setbg(&mc, bce); MFixLine(p, y, &mc); ml = p->w_mlines + y; if (mc.attr) for (x = xs; x <= xe; x++) ml->attr[x] = mc.attr; if (mc.color) for (x = xs; x <= xe; x++) ml->color[x] = mc.color; if (mc.colorx) for (x = xs; x <= xe; x++) ml->colorx[x] = mc.colorx; } static void WAddLineToHist(struct win *wp, struct mline *ml) { register unsigned char *q, *o; struct mline *hml; if (wp->w_histheight == 0) return; hml = &wp->w_hlines[wp->w_histidx]; q = ml->image; ml->image = hml->image; hml->image = q; q = ml->attr; o = hml->attr; hml->attr = q; ml->attr = null; if (o != null) free(o); q = ml->font; o = hml->font; hml->font = q; ml->font = null; if (o != null) free(o); q = ml->fontx; o = hml->fontx; hml->fontx = q; ml->fontx = null; if (o != null) free(o); q = ml->color; o = hml->color; hml->color = q; ml->color = null; if (o != null) free(o); q = ml->colorx; o = hml->colorx; hml->colorx = q; ml->colorx = null; if (o != null) free(o); if (++wp->w_histidx >= wp->w_histheight) wp->w_histidx = 0; } int MFindUsedLine(struct win *p, int ye, int ys) { int y; struct mline *ml = p->w_mlines + ye; debug2("MFindUsedLine: %d %d\n", ye, ys); for (y = ye; y >= ys; y--, ml--) { if (memcmp((char*)ml->image, blank, p->w_width)) break; if (ml->attr != null && memcmp((char*)ml->attr, null, p->w_width)) break; if (ml->color != null && memcmp((char*)ml->color, null, p->w_width)) break; if (ml->colorx != null && memcmp((char*)ml->colorx, null, p->w_width)) break; if (p->w_encoding == UTF8) { if (ml->font != null && bcmp((char*)ml->font, null, p->w_width)) break; if (ml->fontx != null && bcmp((char*)ml->fontx, null, p->w_width)) break; } } debug1("MFindUsedLine returning %d\n", y); return y; } /* *====================================================================* *====================================================================* */ /* * Tricky: send only one bell even if the window is displayed * more than once. */ void WBell(struct win *p, int visual) { struct canvas *cv; if (displays == NULL) p->w_bell = BELL_DONE; for (display = displays; display; display = display->d_next) { for (cv = D_cvlist; cv; cv = cv->c_next) if (cv->c_layer->l_bottom == &p->w_layer) break; if (cv && !visual) AddCStr(D_BL); else if (cv && D_VB) AddCStr(D_VB); else p->w_bell = visual ? BELL_VISUAL : BELL_FOUND; } } /* * This should be reverse video. * Only change video if window is fore. * Because it is used in some termcaps to emulate * a visual bell we do this hack here. * (screen uses \Eg as special vbell sequence) */ static void WReverseVideo(struct win *p, int on) { struct canvas *cv; for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext) { display = cv->c_display; if (cv != D_forecv) continue; ReverseVideo(on); if (!on && p->w_revvid && !D_CVR) { if (D_VB) AddCStr(D_VB); else p->w_bell = BELL_VISUAL; } } } void WMsg(struct win *p, int err, char *str) { struct layer *flayer; struct layer *oldflayer = flayer; flayer = &p->w_layer; LMsg(err, "%s", str); flayer = oldflayer; } void WChangeSize(struct win *p, int w, int h) { int wok = 0; struct canvas *cv; if (p->w_layer.l_cvlist == 0) { /* window not displayed -> works always */ ChangeWindowSize(p, w, h, p->w_histheight); return; } for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext) { display = cv->c_display; if (p != D_fore) continue; /* change only fore */ if (D_CWS) break; if (D_CZ0 && (w == Z0width || w == Z1width)) wok = 1; } if (cv == 0 && wok == 0) /* can't change any display */ return; if (!D_CWS) h = p->w_height; ChangeWindowSize(p, w, h, p->w_histheight); for (display = displays; display; display = display->d_next) { if (p == D_fore) { if (D_cvlist && D_cvlist->c_next == 0) ResizeDisplay(w, h); else ResizeDisplay(w, D_height); ResizeLayersToCanvases(); /* XXX Hmm ? */ continue; } for (cv = D_cvlist; cv; cv = cv->c_next) if (cv->c_layer->l_bottom == &p->w_layer) break; if (cv) Redisplay(0); } } static int WindowChangedCheck(char *s, int what, int *hp) { int h = 0; int l; while(*s) { if (*s++ != (hp ? '%' : '\005')) continue; l = 0; s += (*s == '+'); s += (*s == '-'); while (*s >= '0' && *s <= '9') s++; if (*s == 'L') { s++; l = 0x100; } if (*s == 'h') h = 1; if (*s == what || ((*s | l) == what) || what == 'd') break; if (*s) s++; } if (hp) *hp = h; return *s ? 1 : 0; } void WindowChanged(struct win *p, int what) { int inwstr, inhstr, inlstr; int inwstrh = 0, inhstrh = 0, inlstrh = 0; int got, ox, oy; struct display *olddisplay = display; struct canvas *cv; inwstr = inhstr = 0; if (what == 'f') { WindowChanged((struct win *)0, 'w'|0x100); WindowChanged((struct win *)0, 'W'|0x100); } if (what) { inwstr = WindowChangedCheck(captionstring, what, &inwstrh); inhstr = WindowChangedCheck(hstatusstring, what, &inhstrh); inlstr = WindowChangedCheck(wliststr, what, &inlstrh); } else { inwstr = inhstr = 0; inlstr = 1; } if (p == 0) { for (display = displays; display; display = display->d_next) { ox = D_x; oy = D_y; for (cv = D_cvlist; cv; cv = cv->c_next) { if (inlstr || (inlstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) WListUpdatecv(cv, (struct win *)0); p = Layer2Window(cv->c_layer); if (inwstr || (inwstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) if (cv->c_ye + 1 < D_height) RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); } p = D_fore; if (inhstr || (inhstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0))) RefreshHStatus(); if (ox != -1 && oy != -1) GotoPos(ox, oy); } display = olddisplay; return; } if (p->w_hstatus && *p->w_hstatus && (inwstrh || inhstrh || inlstrh) && WindowChangedCheck(p->w_hstatus, what, (int *)0)) { inwstr |= inwstrh; inhstr |= inhstrh; inlstr |= inlstrh; } if (!inwstr && !inhstr && !inlstr) return; for (display = displays; display; display = display->d_next) { got = 0; ox = D_x; oy = D_y; for (cv = D_cvlist; cv; cv = cv->c_next) { if (inlstr) WListUpdatecv(cv, p); if (Layer2Window(cv->c_layer) != p) continue; got = 1; if (inwstr && cv->c_ye + 1 < D_height) RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0); } if (got && inhstr && p == D_fore) RefreshHStatus(); if (ox != -1 && oy != -1) GotoPos(ox, oy); } display = olddisplay; }