14 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15 #define CL_VT100 0x0002 /* VT100 */
16 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102 0x0008 /* VT102 */
18 #define CL_VT220 0x0010 /* VT220 */
19 #define CL_VT320 0x0020 /* VT320 */
20 #define CL_VT420 0x0040 /* VT420 */
21 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
27 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29 #define TM_VT102 (TM_VT100AVO|CL_VT102)
30 #define TM_VT220 (TM_VT102|CL_VT220)
31 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level = TM_PUTTY;
51 static tree234 *scrollback; /* lines scrolled off top of screen */
52 static tree234 *screen; /* lines on primary screen */
53 static tree234 *alt_screen; /* lines on alternate screen */
54 static int disptop; /* distance scrolled back (0 or -ve) */
56 static unsigned long *cpos; /* cursor position (convenience) */
58 static unsigned long *disptext; /* buffer of text on real screen */
59 static unsigned long *dispcurs; /* location of cursor on real screen */
60 static unsigned long curstype; /* type of cursor on real screen */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime *next;
68 static struct beeptime *beephead, *beeptail;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89 static pos curs; /* cursor */
90 static pos savecurs; /* saved cursor position */
91 static int marg_t, marg_b; /* scroll margins */
92 static int dec_om; /* DEC origin mode flag */
93 static int wrap, wrapnext; /* wrap flags */
94 static int insert; /* insert-mode flag */
95 static int cset; /* 0 or 1: which char set */
96 static int save_cset, save_csattr; /* saved with cursor position */
97 static int rvideo; /* global reverse video flag */
98 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
99 static int cursor_on; /* cursor enabled flag */
100 static int reset_132; /* Flag ESC c resets to 80 cols */
101 static int use_bce; /* Use Background coloured erase */
102 static int blinker; /* When blinking is the cursor on ? */
103 static int tblinker; /* When the blinking text is on */
104 static int blink_is_real; /* Actually blink blinking text */
105 static int term_echoing; /* Does terminal want local echo? */
106 static int term_editing; /* Does terminal want local edit? */
107 static int vt52_bold; /* Force bold on non-bold colours */
108 static int utf_state; /* Is there a pending UTF-8 character */
109 static int utf_char; /* and what is it so far. */
110 static int utf_size; /* The size of the UTF character. */
112 static int xterm_mouse; /* send mouse messages to app */
114 static unsigned long cset_attr[2];
117 * Saved settings on the alternate screen.
119 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
120 static int alt_t, alt_b;
121 static int alt_which;
123 #define ARGS_MAX 32 /* max # of esc sequence arguments */
124 #define ARG_DEFAULT 0 /* if an arg isn't specified */
125 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
126 static int esc_args[ARGS_MAX];
127 static int esc_nargs;
128 static int esc_query;
129 #define ANSI(x,y) ((x)+((y)<<8))
130 #define ANSI_QUE(x) ANSI(x,TRUE)
132 #define OSC_STR_MAX 2048
133 static int osc_strlen;
134 static char osc_string[OSC_STR_MAX + 1];
137 static char id_string[1024] = "\033[?6c";
139 static unsigned char *tabs;
151 OSC_STRING, OSC_MAYBE_ST,
160 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
163 SM_CHAR, SM_WORD, SM_LINE
165 static pos selstart, selend, selanchor;
167 static short wordness[256] = {
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
170 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
172 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
180 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
181 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
186 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
187 static wchar_t sel_nl[] = SEL_NL;
188 static wchar_t *paste_buffer = 0;
189 static int paste_len, paste_pos, paste_hold;
192 * Internal prototypes.
194 static void do_paint(Context, int);
195 static void erase_lots(int, int, int);
196 static void swap_screen(int);
197 static void update_sbar(void);
198 static void deselect(void);
199 /* log session to file stuff ... */
200 static FILE *lgfp = NULL;
201 static void logtraffic(unsigned char c, int logmode);
204 * Resize a line to make it `cols' columns wide.
206 unsigned long *resizeline(unsigned long *line, int cols)
209 unsigned long lineattrs;
211 if (line[0] != (unsigned long)cols) {
213 * This line is the wrong length, which probably means it
214 * hasn't been accessed since a resize. Resize it now.
217 lineattrs = line[oldlen + 1];
218 line = srealloc(line, TSIZE * (2 + cols));
220 for (i = oldlen; i < cols; i++)
221 line[i + 1] = ERASE_CHAR;
222 line[cols + 1] = lineattrs & LATTR_MODE;
229 * Retrieve a line of the screen or of the scrollback, according to
230 * whether the y coordinate is non-negative or negative
233 unsigned long *lineptr(int y, int lineno)
235 unsigned long *line, *newline;
243 whichtree = scrollback;
244 treeindex = y + count234(scrollback);
246 line = index234(whichtree, treeindex);
248 /* We assume that we don't screw up and retrieve something out of range. */
249 assert(line != NULL);
251 newline = resizeline(line, cols);
252 if (newline != line) {
253 delpos234(whichtree, treeindex);
254 addpos234(whichtree, newline, treeindex);
260 #define lineptr(x) lineptr(x,__LINE__)
262 * Set up power-on settings for the terminal.
264 static void power_on(void)
266 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
269 alt_b = marg_b = rows - 1;
274 for (i = 0; i < cols; i++)
275 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
277 alt_om = dec_om = cfg.dec_om;
278 alt_wnext = wrapnext = alt_ins = insert = FALSE;
279 alt_wrap = wrap = cfg.wrap_mode;
281 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
286 save_attr = curr_attr = ATTR_DEFAULT;
287 term_editing = term_echoing = FALSE;
288 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
289 app_cursor_keys = cfg.app_cursor;
290 app_keypad_keys = cfg.app_keypad;
292 blink_is_real = cfg.blinktext;
293 erase_char = ERASE_CHAR;
297 for (i = 0; i < 256; i++)
298 wordness[i] = cfg.wordness[i];
302 erase_lots(FALSE, TRUE, TRUE);
304 erase_lots(FALSE, TRUE, TRUE);
309 * Force a screen update.
311 void term_update(void)
318 if ((seen_key_event && (cfg.scroll_on_key)) ||
319 (seen_disp_event && (cfg.scroll_on_disp))) {
320 disptop = 0; /* return to main screen */
321 seen_disp_event = seen_key_event = 0;
324 sys_cursor(curs.x, curs.y - disptop);
330 * Same as power_on(), but an external function.
332 void term_pwron(void)
342 * Clear the scrollback.
344 void term_clrsb(void)
348 while ((line = delpos234(scrollback, 0)) != NULL) {
355 * Initialise the terminal.
359 screen = alt_screen = scrollback = NULL;
361 disptext = dispcurs = NULL;
366 beephead = beeptail = NULL;
369 beep_overloaded = FALSE;
373 * Set up the terminal for a given size.
375 void term_size(int newrows, int newcols, int newsavelines)
378 unsigned long *newdisp, *line;
381 int save_alt_which = alt_which;
383 if (newrows == rows && newcols == cols && newsavelines == savelines)
384 return; /* nothing to do */
390 alt_b = marg_b = newrows - 1;
393 scrollback = newtree234(NULL);
394 screen = newtree234(NULL);
399 * Resize the screen and scrollback. We only need to shift
400 * lines around within our data structures, because lineptr()
401 * will take care of resizing each individual line if
404 * - If the new screen and the old screen differ in length, we
405 * must shunt some lines in from the scrollback or out to
408 * - If doing that fails to provide us with enough material to
409 * fill the new screen (i.e. the number of rows needed in
410 * the new screen exceeds the total number in the previous
411 * screen+scrollback), we must invent some blank lines to
414 * - Then, if the new scrollback length is less than the
415 * amount of scrollback we actually have, we must throw some
418 sblen = count234(scrollback);
419 /* Do this loop to expand the screen if newrows > rows */
420 for (i = rows; i < newrows; i++) {
422 line = delpos234(scrollback, --sblen);
424 line = smalloc(TSIZE * (newcols + 2));
426 for (j = 0; j <= newcols; j++)
427 line[j + 1] = ERASE_CHAR;
429 addpos234(screen, line, 0);
431 /* Do this loop to shrink the screen if newrows < rows */
432 for (i = newrows; i < rows; i++) {
433 line = delpos234(screen, 0);
434 addpos234(scrollback, line, sblen++);
436 assert(count234(screen) == newrows);
437 while (sblen > newsavelines) {
438 line = delpos234(scrollback, 0);
442 assert(count234(scrollback) <= newsavelines);
445 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
446 for (i = 0; i < newrows * (newcols + 1); i++)
447 newdisp[i] = ATTR_INVALID;
452 newalt = newtree234(NULL);
453 for (i = 0; i < newrows; i++) {
454 line = smalloc(TSIZE * (newcols + 2));
456 for (j = 0; j <= newcols; j++)
457 line[j + 1] = erase_char;
458 addpos234(newalt, line, i);
461 while (NULL != (line = delpos234(alt_screen, 0)))
463 freetree234(alt_screen);
467 tabs = srealloc(tabs, newcols * sizeof(*tabs));
470 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
471 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
475 curs.y += newrows - rows;
478 if (curs.y >= newrows)
479 curs.y = newrows - 1;
480 if (curs.x >= newcols)
481 curs.x = newcols - 1;
483 wrapnext = alt_wnext = FALSE;
487 savelines = newsavelines;
490 swap_screen(save_alt_which);
499 static void swap_screen(int which)
504 if (which == alt_which)
531 wrapnext = alt_wnext;
544 * Update the scroll bar.
546 static void update_sbar(void)
550 nscroll = count234(scrollback);
552 set_sbar(nscroll + rows, nscroll + disptop, rows);
556 * Check whether the region bounded by the two pointers intersects
557 * the scroll region, and de-select the on-screen selection if so.
559 static void check_selection(pos from, pos to)
561 if (poslt(from, selend) && poslt(selstart, to))
566 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
567 * for backward.) `sb' is TRUE if the scrolling is permitted to
568 * affect the scrollback buffer.
570 * NB this function invalidates all pointers into lines of the
571 * screen data structures. In particular, you MUST call fix_cpos
572 * after calling scroll() and before doing anything else that
573 * uses the cpos shortcut pointer.
575 static void scroll(int topline, int botline, int lines, int sb)
577 unsigned long *line, *line2;
580 if (topline != 0 || alt_which != 0)
585 line = delpos234(screen, botline);
586 line = resizeline(line, cols);
587 for (i = 0; i < cols; i++)
588 line[i + 1] = erase_char;
590 addpos234(screen, line, topline);
592 if (selstart.y >= topline && selstart.y <= botline) {
594 if (selstart.y > botline) {
595 selstart.y = botline;
599 if (selend.y >= topline && selend.y <= botline) {
601 if (selend.y > botline) {
611 line = delpos234(screen, topline);
612 if (sb && savelines > 0) {
613 int sblen = count234(scrollback);
615 * We must add this line to the scrollback. We'll
616 * remove a line from the top of the scrollback to
617 * replace it, or allocate a new one if the
618 * scrollback isn't full.
620 if (sblen == savelines) {
621 sblen--, line2 = delpos234(scrollback, 0);
623 line2 = smalloc(TSIZE * (cols + 2));
626 addpos234(scrollback, line, sblen);
629 line = resizeline(line, cols);
630 for (i = 0; i < cols; i++)
631 line[i + 1] = erase_char;
633 addpos234(screen, line, botline);
635 if (selstart.y >= topline && selstart.y <= botline) {
637 if (selstart.y < topline) {
638 selstart.y = topline;
642 if (selend.y >= topline && selend.y <= botline) {
644 if (selend.y < topline) {
656 * Move the cursor to a given position, clipping at boundaries. We
657 * may or may not want to clip at the scroll margin: marg_clip is 0
658 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
659 * even _being_ outside the margins.
661 static void move(int x, int y, int marg_clip)
668 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
670 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
684 * Save or restore the cursor and SGR mode.
686 static void save_cursor(int save)
690 save_attr = curr_attr;
692 save_csattr = cset_attr[cset];
695 /* Make sure the window hasn't shrunk since the save */
701 curr_attr = save_attr;
703 cset_attr[cset] = save_csattr;
706 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
711 * Erase a large portion of the screen: the whole screen, or the
712 * whole line, or parts thereof.
714 static void erase_lots(int line_only, int from_begin, int to_end)
718 unsigned long *ldata;
740 check_selection(start, end);
742 /* Clear screen also forces a full window redraw, just in case. */
743 if (start.y == 0 && start.x == 0 && end.y == rows)
746 ldata = lineptr(start.y);
747 while (poslt(start, end)) {
748 if (start.x == cols && !erase_lattr)
749 ldata[start.x] &= ~LATTR_WRAPPED;
751 ldata[start.x] = erase_char;
752 if (incpos(start) && start.y < rows)
753 ldata = lineptr(start.y);
758 * Insert or delete characters within the current line. n is +ve if
759 * insertion is desired, and -ve for deletion.
761 static void insch(int n)
763 int dir = (n < 0 ? -1 : +1);
766 unsigned long *ldata;
768 n = (n < 0 ? -n : n);
769 if (n > cols - curs.x)
771 m = cols - curs.x - n;
773 cursplus.x = curs.x + n;
774 check_selection(curs, cursplus);
775 ldata = lineptr(curs.y);
777 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
779 ldata[curs.x + m++] = erase_char;
781 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
783 ldata[curs.x + n] = erase_char;
788 * Toggle terminal mode `mode' to state `state'. (`query' indicates
789 * whether the mode is a DEC private one or a normal one.)
791 static void toggle_mode(int mode, int query, int state)
797 case 1: /* application cursor keys */
798 app_cursor_keys = state;
800 case 2: /* VT52 mode */
803 blink_is_real = FALSE;
806 blink_is_real = cfg.blinktext;
809 case 3: /* 80/132 columns */
811 request_resize(state ? 132 : 80, rows, 1);
814 case 5: /* reverse video */
816 * Toggle reverse video. If we receive an OFF within the
817 * visual bell timeout period after an ON, we trigger an
818 * effective visual bell, so that ESC[?5hESC[?5l will
819 * always be an actually _visible_ visual bell.
821 ticks = GetTickCount();
822 if (rvideo && !state && /* we're turning it off */
823 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
824 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
825 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
826 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
827 } else if (!rvideo && state) {
828 /* This is an ON, so we notice the time and save it. */
829 rvbell_timeout = ticks + VBELL_TIMEOUT;
832 seen_disp_event = TRUE;
836 case 6: /* DEC origin mode */
839 case 7: /* auto wrap */
842 case 8: /* auto key repeat */
845 case 10: /* set local edit mode */
846 term_editing = state;
847 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
849 case 25: /* enable/disable cursor */
850 compatibility2(OTHER, VT220);
852 seen_disp_event = TRUE;
854 case 47: /* alternate screen */
855 compatibility(OTHER);
860 case 1000: /* xterm mouse 1 */
861 xterm_mouse = state ? 1 : 0;
862 set_raw_mouse_mode(state);
864 case 1002: /* xterm mouse 2 */
865 xterm_mouse = state ? 2 : 0;
866 set_raw_mouse_mode(state);
870 case 4: /* set insert mode */
871 compatibility(VT102);
874 case 12: /* set echo mode */
875 term_echoing = !state;
876 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
878 case 20: /* Return sends ... */
879 cr_lf_return = state;
881 case 34: /* Make cursor BIG */
882 compatibility2(OTHER, VT220);
888 * Process an OSC sequence: set window title or icon name.
890 static void do_osc(void)
894 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
896 osc_string[osc_strlen] = '\0';
897 switch (esc_args[0]) {
900 set_icon(osc_string);
901 if (esc_args[0] == 1)
903 /* fall through: parameter 0 means set both */
906 set_title(osc_string);
913 * Remove everything currently in `inbuf' and stick it up on the
914 * in-memory display. There's a big state machine in here to
915 * process escape sequences...
921 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
922 c = inbuf[inbuf_reap];
925 * Optionally log the session traffic to a file. Useful for
926 * debugging and possibly also useful for actual logging.
928 logtraffic((unsigned char) c, LGTYP_DEBUG);
930 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
931 * be able to display 8-bit characters, but I'll let that go 'cause
935 /* First see about all those translations. */
936 if (termstate == TOPLEVEL) {
941 /* I know; gotos are evil. This one is really bad!
942 * But before you try removing it follow the path of the
943 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
946 if (cfg.no_vt_graph_with_utf8) break;
949 } else if ((c & 0xe0) == 0xc0) {
950 utf_size = utf_state = 1;
951 utf_char = (c & 0x1f);
952 } else if ((c & 0xf0) == 0xe0) {
953 utf_size = utf_state = 2;
954 utf_char = (c & 0x0f);
955 } else if ((c & 0xf8) == 0xf0) {
956 utf_size = utf_state = 3;
957 utf_char = (c & 0x07);
958 } else if ((c & 0xfc) == 0xf8) {
959 utf_size = utf_state = 4;
960 utf_char = (c & 0x03);
961 } else if ((c & 0xfe) == 0xfc) {
962 utf_size = utf_state = 5;
963 utf_char = (c & 0x01);
974 if ((c & 0xC0) != 0x80) {
975 inbuf_reap--; /* This causes the faulting character */
976 c = UCSERR; /* to be logged twice - not really a */
977 utf_state = 0; /* serious problem. */
980 utf_char = (utf_char << 6) | (c & 0x3f);
986 /* Is somebody trying to be evil! */
988 (c < 0x800 && utf_size >= 2) ||
989 (c < 0x10000 && utf_size >= 3) ||
990 (c < 0x200000 && utf_size >= 4) ||
991 (c < 0x4000000 && utf_size >= 5))
994 /* Unicode line separator and paragraph separator are CR-LF */
995 if (c == 0x2028 || c == 0x2029)
998 /* High controls are probably a Baaad idea too. */
1002 /* The UTF-16 surrogates are not nice either. */
1003 /* The standard give the option of decoding these:
1004 * I don't want to! */
1005 if (c >= 0xD800 && c < 0xE000)
1008 /* ISO 10646 characters now limited to UTF-16 range. */
1012 /* This is currently a TagPhobic application.. */
1013 if (c >= 0xE0000 && c <= 0xE007F)
1016 /* U+FEFF is best seen as a null. */
1019 /* But U+FFFE is an error. */
1020 if (c == 0xFFFE || c == 0xFFFF)
1023 /* Oops this is a 16bit implementation */
1029 switch (cset_attr[cset]) {
1031 * Linedraw characters are different from 'ESC ( B'
1032 * only for a small range. For ones outside that
1033 * range, make sure we use the same font as well as
1034 * the same encoding.
1037 if (unitab_ctrl[c] != 0xFF)
1040 c = ((unsigned char) c) | ATTR_LINEDRW;
1044 /* If UK-ASCII, make the '#' a LineDraw Pound */
1046 c = '}' | ATTR_LINEDRW;
1049 /*FALLTHROUGH*/ case ATTR_ASCII:
1050 if (unitab_ctrl[c] != 0xFF)
1053 c = ((unsigned char) c) | ATTR_ASCII;
1059 /* How about C1 controls ? */
1060 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1061 has_compat(VT220)) {
1062 termstate = SEEN_ESC;
1064 c = '@' + (c & 0x1F);
1067 /* Or the GL control. */
1068 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1069 if (curs.x && !wrapnext)
1073 *cpos = (' ' | curr_attr | ATTR_ASCII);
1075 /* Or normal C0 controls. */
1076 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1078 case '\005': /* terminal type query */
1079 /* Strictly speaking this is VT100 but a VT100 defaults to
1080 * no response. Other terminals respond at their option.
1082 * Don't put a CR in the default string as this tends to
1083 * upset some weird software.
1085 * An xterm returns "xterm" (5 characters)
1087 compatibility(ANSIMIN);
1089 char abuf[256], *s, *d;
1091 for (s = cfg.answerback, d = abuf; *s; s++) {
1093 if (*s >= 'a' && *s <= 'z')
1094 *d++ = (*s - ('a' - 1));
1095 else if ((*s >= '@' && *s <= '_') ||
1096 *s == '?' || (*s & 0x80))
1101 } else if (*s == '^') {
1106 lpage_send(CP_ACP, abuf, d - abuf);
1111 struct beeptime *newbeep;
1114 ticks = GetTickCount();
1116 if (!beep_overloaded) {
1117 newbeep = smalloc(sizeof(struct beeptime));
1118 newbeep->ticks = ticks;
1119 newbeep->next = NULL;
1123 beeptail->next = newbeep;
1129 * Throw out any beeps that happened more than
1133 beephead->ticks < ticks - cfg.bellovl_t) {
1134 struct beeptime *tmp = beephead;
1135 beephead = tmp->next;
1142 if (cfg.bellovl && beep_overloaded &&
1143 ticks - lastbeep >= cfg.bellovl_s) {
1145 * If we're currently overloaded and the
1146 * last beep was more than s seconds ago,
1147 * leave overload mode.
1149 beep_overloaded = FALSE;
1150 } else if (cfg.bellovl && !beep_overloaded &&
1151 nbeeps >= cfg.bellovl_n) {
1153 * Now, if we have n or more beeps
1154 * remaining in the queue, go into overload
1157 beep_overloaded = TRUE;
1162 * Perform an actual beep if we're not overloaded.
1164 if (!cfg.bellovl || !beep_overloaded) {
1166 if (cfg.beep == BELL_VISUAL) {
1168 vbell_timeout = ticks + VBELL_TIMEOUT;
1176 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1177 else if (curs.x == 0 && curs.y > 0)
1178 curs.x = cols - 1, curs.y--;
1184 seen_disp_event = TRUE;
1187 compatibility(VT100);
1191 compatibility(VT100);
1196 termstate = VT52_ESC;
1198 compatibility(ANSIMIN);
1199 termstate = SEEN_ESC;
1207 seen_disp_event = TRUE;
1209 logtraffic((unsigned char) c, LGTYP_ASCII);
1212 if (has_compat(SCOANSI)) {
1214 erase_lots(FALSE, FALSE, TRUE);
1217 seen_disp_event = 1;
1221 compatibility(VT100);
1223 if (curs.y == marg_b)
1224 scroll(marg_t, marg_b, 1, TRUE);
1225 else if (curs.y < rows - 1)
1231 seen_disp_event = 1;
1233 logtraffic((unsigned char) c, LGTYP_ASCII);
1237 pos old_curs = curs;
1238 unsigned long *ldata = lineptr(curs.y);
1242 } while (curs.x < cols - 1 && !tabs[curs.x]);
1244 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1245 if (curs.x >= cols / 2)
1246 curs.x = cols / 2 - 1;
1253 check_selection(old_curs, curs);
1255 seen_disp_event = TRUE;
1259 switch (termstate) {
1261 /* Only graphic characters get this far, ctrls are stripped above */
1262 if (wrapnext && wrap) {
1263 cpos[1] |= LATTR_WRAPPED;
1264 if (curs.y == marg_b)
1265 scroll(marg_t, marg_b, 1, TRUE);
1266 else if (curs.y < rows - 1)
1274 if (selstate != NO_SELECTION) {
1275 pos cursplus = curs;
1277 check_selection(curs, cursplus);
1279 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1280 logtraffic((unsigned char) c, LGTYP_ASCII);
1282 extern int wcwidth(wchar_t ucs);
1287 width = wcwidth((wchar_t) c);
1290 if (curs.x + 1 != cols) {
1291 *cpos++ = c | ATTR_WIDE | curr_attr;
1292 *cpos++ = UCSWIDE | curr_attr;
1297 *cpos++ = c | curr_attr;
1304 if (curs.x == cols) {
1308 if (wrap && vt52_mode) {
1309 cpos[1] |= LATTR_WRAPPED;
1310 if (curs.y == marg_b)
1311 scroll(marg_t, marg_b, 1, TRUE);
1312 else if (curs.y < rows - 1)
1319 seen_disp_event = 1;
1324 * This state is virtually identical to SEEN_ESC, with the
1325 * exception that we have an OSC sequence in the pipeline,
1326 * and _if_ we see a backslash, we process it.
1330 termstate = TOPLEVEL;
1333 /* else fall through */
1335 if (c >= ' ' && c <= '/') {
1342 termstate = TOPLEVEL;
1343 switch (ANSI(c, esc_query)) {
1344 case '[': /* enter CSI mode */
1345 termstate = SEEN_CSI;
1347 esc_args[0] = ARG_DEFAULT;
1350 case ']': /* xterm escape sequences */
1351 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1352 compatibility(OTHER);
1353 termstate = SEEN_OSC;
1356 case '7': /* save cursor */
1357 compatibility(VT100);
1360 case '8': /* restore cursor */
1361 compatibility(VT100);
1363 seen_disp_event = TRUE;
1366 compatibility(VT100);
1367 app_keypad_keys = TRUE;
1370 compatibility(VT100);
1371 app_keypad_keys = FALSE;
1373 case 'D': /* exactly equivalent to LF */
1374 compatibility(VT100);
1375 if (curs.y == marg_b)
1376 scroll(marg_t, marg_b, 1, TRUE);
1377 else if (curs.y < rows - 1)
1381 seen_disp_event = TRUE;
1383 case 'E': /* exactly equivalent to CR-LF */
1384 compatibility(VT100);
1386 if (curs.y == marg_b)
1387 scroll(marg_t, marg_b, 1, TRUE);
1388 else if (curs.y < rows - 1)
1392 seen_disp_event = TRUE;
1394 case 'M': /* reverse index - backwards LF */
1395 compatibility(VT100);
1396 if (curs.y == marg_t)
1397 scroll(marg_t, marg_b, -1, TRUE);
1398 else if (curs.y > 0)
1402 seen_disp_event = TRUE;
1404 case 'Z': /* terminal type query */
1405 compatibility(VT100);
1406 ldisc_send(id_string, strlen(id_string));
1408 case 'c': /* restore power-on settings */
1409 compatibility(VT100);
1412 request_resize(80, rows, 1);
1417 seen_disp_event = TRUE;
1419 case 'H': /* set a tab */
1420 compatibility(VT100);
1421 tabs[curs.x] = TRUE;
1424 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1425 compatibility(VT100);
1427 unsigned long *ldata;
1431 for (i = 0; i < rows; i++) {
1433 for (j = 0; j < cols; j++)
1434 ldata[j] = ATTR_DEFAULT | 'E';
1438 seen_disp_event = TRUE;
1439 scrtop.x = scrtop.y = 0;
1442 check_selection(scrtop, scrbot);
1446 case ANSI('3', '#'):
1447 case ANSI('4', '#'):
1448 case ANSI('5', '#'):
1449 case ANSI('6', '#'):
1450 compatibility(VT100);
1452 unsigned long nlattr;
1453 unsigned long *ldata;
1454 switch (ANSI(c, esc_query)) {
1455 case ANSI('3', '#'):
1458 case ANSI('4', '#'):
1461 case ANSI('5', '#'):
1462 nlattr = LATTR_NORM;
1464 default: /* spiritually case ANSI('6', '#'): */
1465 nlattr = LATTR_WIDE;
1468 ldata = lineptr(curs.y);
1469 ldata[cols] &= ~LATTR_MODE;
1470 ldata[cols] |= nlattr;
1474 case ANSI('A', '('):
1475 compatibility(VT100);
1476 cset_attr[0] = ATTR_GBCHR;
1478 case ANSI('B', '('):
1479 compatibility(VT100);
1480 cset_attr[0] = ATTR_ASCII;
1482 case ANSI('0', '('):
1483 compatibility(VT100);
1484 cset_attr[0] = ATTR_LINEDRW;
1487 case ANSI('A', ')'):
1488 compatibility(VT100);
1489 cset_attr[1] = ATTR_GBCHR;
1491 case ANSI('B', ')'):
1492 compatibility(VT100);
1493 cset_attr[1] = ATTR_ASCII;
1495 case ANSI('0', ')'):
1496 compatibility(VT100);
1497 cset_attr[1] = ATTR_LINEDRW;
1500 case ANSI('8', '%'): /* Old Linux code */
1501 case ANSI('G', '%'):
1502 compatibility(OTHER);
1505 case ANSI('@', '%'):
1506 compatibility(OTHER);
1507 if (line_codepage != CP_UTF8)
1513 termstate = TOPLEVEL; /* default */
1515 if (esc_nargs <= ARGS_MAX) {
1516 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1517 esc_args[esc_nargs - 1] = 0;
1518 esc_args[esc_nargs - 1] =
1519 10 * esc_args[esc_nargs - 1] + c - '0';
1521 termstate = SEEN_CSI;
1522 } else if (c == ';') {
1523 if (++esc_nargs <= ARGS_MAX)
1524 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1525 termstate = SEEN_CSI;
1526 } else if (c < '@') {
1533 termstate = SEEN_CSI;
1535 switch (ANSI(c, esc_query)) {
1536 case 'A': /* move up N lines */
1537 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1538 seen_disp_event = TRUE;
1540 case 'e': /* move down N lines */
1541 compatibility(ANSI);
1544 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1545 seen_disp_event = TRUE;
1547 case ANSI('c', '>'): /* report xterm version */
1548 compatibility(OTHER);
1549 /* this reports xterm version 136 so that VIM can
1550 use the drag messages from the mouse reporting */
1551 ldisc_send("\033[>0;136;0c", 11);
1553 case 'a': /* move right N cols */
1554 compatibility(ANSI);
1557 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1558 seen_disp_event = TRUE;
1560 case 'D': /* move left N cols */
1561 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1562 seen_disp_event = TRUE;
1564 case 'E': /* move down N lines and CR */
1565 compatibility(ANSI);
1566 move(0, curs.y + def(esc_args[0], 1), 1);
1567 seen_disp_event = TRUE;
1569 case 'F': /* move up N lines and CR */
1570 compatibility(ANSI);
1571 move(0, curs.y - def(esc_args[0], 1), 1);
1572 seen_disp_event = TRUE;
1575 case '`': /* set horizontal posn */
1576 compatibility(ANSI);
1577 move(def(esc_args[0], 1) - 1, curs.y, 0);
1578 seen_disp_event = TRUE;
1580 case 'd': /* set vertical posn */
1581 compatibility(ANSI);
1583 (dec_om ? marg_t : 0) + def(esc_args[0],
1586 seen_disp_event = TRUE;
1589 case 'f': /* set horz and vert posns at once */
1591 esc_args[1] = ARG_DEFAULT;
1592 move(def(esc_args[1], 1) - 1,
1593 (dec_om ? marg_t : 0) + def(esc_args[0],
1596 seen_disp_event = TRUE;
1598 case 'J': /* erase screen or parts of it */
1600 unsigned int i = def(esc_args[0], 0) + 1;
1603 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1606 seen_disp_event = TRUE;
1608 case 'K': /* erase line or parts of it */
1610 unsigned int i = def(esc_args[0], 0) + 1;
1613 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1615 seen_disp_event = TRUE;
1617 case 'L': /* insert lines */
1618 compatibility(VT102);
1619 if (curs.y <= marg_b)
1620 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1623 seen_disp_event = TRUE;
1625 case 'M': /* delete lines */
1626 compatibility(VT102);
1627 if (curs.y <= marg_b)
1628 scroll(curs.y, marg_b, def(esc_args[0], 1),
1631 seen_disp_event = TRUE;
1633 case '@': /* insert chars */
1634 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1635 compatibility(VT102);
1636 insch(def(esc_args[0], 1));
1637 seen_disp_event = TRUE;
1639 case 'P': /* delete chars */
1640 compatibility(VT102);
1641 insch(-def(esc_args[0], 1));
1642 seen_disp_event = TRUE;
1644 case 'c': /* terminal type query */
1645 compatibility(VT100);
1646 /* This is the response for a VT102 */
1647 ldisc_send(id_string, strlen(id_string));
1649 case 'n': /* cursor position query */
1650 if (esc_args[0] == 6) {
1652 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1654 ldisc_send(buf, strlen(buf));
1655 } else if (esc_args[0] == 5) {
1656 ldisc_send("\033[0n", 4);
1659 case 'h': /* toggle modes to high */
1661 compatibility(VT100);
1664 for (i = 0; i < esc_nargs; i++)
1665 toggle_mode(esc_args[i], esc_query, TRUE);
1668 case 'l': /* toggle modes to low */
1670 compatibility(VT100);
1673 for (i = 0; i < esc_nargs; i++)
1674 toggle_mode(esc_args[i], esc_query, FALSE);
1677 case 'g': /* clear tabs */
1678 compatibility(VT100);
1679 if (esc_nargs == 1) {
1680 if (esc_args[0] == 0) {
1681 tabs[curs.x] = FALSE;
1682 } else if (esc_args[0] == 3) {
1684 for (i = 0; i < cols; i++)
1689 case 'r': /* set scroll margins */
1690 compatibility(VT100);
1691 if (esc_nargs <= 2) {
1693 top = def(esc_args[0], 1) - 1;
1694 bot = (esc_nargs <= 1
1696 0 ? rows : def(esc_args[1], rows)) - 1;
1699 /* VTTEST Bug 9 - if region is less than 2 lines
1700 * don't change region.
1702 if (bot - top > 0) {
1707 * I used to think the cursor should be
1708 * placed at the top of the newly marginned
1709 * area. Apparently not: VMS TPU falls over
1712 * Well actually it should for Origin mode - RDB
1714 curs.y = (dec_om ? marg_t : 0);
1716 seen_disp_event = TRUE;
1720 case 'm': /* set graphics rendition */
1723 * A VT100 without the AVO only had one attribute, either
1724 * underline or reverse video depending on the cursor type,
1725 * this was selected by CSI 7m.
1728 * This is sometimes DIM, eg on the GIGI and Linux
1730 * This is sometimes INVIS various ANSI.
1732 * This like 22 disables BOLD, DIM and INVIS
1734 * The ANSI colours appear on any terminal that has colour
1735 * (obviously) but the interaction between sgr0 and the
1736 * colours varies but is usually related to the background
1737 * colour erase item.
1738 * The interaction between colour attributes and the mono
1739 * ones is also very implementation dependent.
1741 * The 39 and 49 attributes are likely to be unimplemented.
1744 for (i = 0; i < esc_nargs; i++) {
1745 switch (def(esc_args[i], 0)) {
1746 case 0: /* restore defaults */
1747 curr_attr = ATTR_DEFAULT;
1749 case 1: /* enable bold */
1750 compatibility(VT100AVO);
1751 curr_attr |= ATTR_BOLD;
1753 case 21: /* (enable double underline) */
1754 compatibility(OTHER);
1755 case 4: /* enable underline */
1756 compatibility(VT100AVO);
1757 curr_attr |= ATTR_UNDER;
1759 case 5: /* enable blink */
1760 compatibility(VT100AVO);
1761 curr_attr |= ATTR_BLINK;
1763 case 7: /* enable reverse video */
1764 curr_attr |= ATTR_REVERSE;
1766 case 22: /* disable bold */
1767 compatibility2(OTHER, VT220);
1768 curr_attr &= ~ATTR_BOLD;
1770 case 24: /* disable underline */
1771 compatibility2(OTHER, VT220);
1772 curr_attr &= ~ATTR_UNDER;
1774 case 25: /* disable blink */
1775 compatibility2(OTHER, VT220);
1776 curr_attr &= ~ATTR_BLINK;
1778 case 27: /* disable reverse video */
1779 compatibility2(OTHER, VT220);
1780 curr_attr &= ~ATTR_REVERSE;
1791 curr_attr &= ~ATTR_FGMASK;
1793 (esc_args[i] - 30) << ATTR_FGSHIFT;
1795 case 39: /* default-foreground */
1796 curr_attr &= ~ATTR_FGMASK;
1797 curr_attr |= ATTR_DEFFG;
1808 curr_attr &= ~ATTR_BGMASK;
1810 (esc_args[i] - 40) << ATTR_BGSHIFT;
1812 case 49: /* default-background */
1813 curr_attr &= ~ATTR_BGMASK;
1814 curr_attr |= ATTR_DEFBG;
1822 (ATTR_FGMASK | ATTR_BGMASK |
1826 case 's': /* save cursor */
1829 case 'u': /* restore cursor */
1831 seen_disp_event = TRUE;
1833 case 't': /* set page size - ie window height */
1835 * VT340/VT420 sequence DECSLPP, DEC only allows values
1836 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1837 * illegal values (eg first arg 1..9) for window changing
1840 compatibility(VT340TEXT);
1842 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1843 request_resize(cols, def(esc_args[0], 24), 0);
1848 compatibility(SCOANSI);
1849 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1852 seen_disp_event = TRUE;
1855 compatibility(SCOANSI);
1856 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1859 seen_disp_event = TRUE;
1861 case ANSI('|', '*'):
1862 /* VT420 sequence DECSNLS
1863 * Set number of lines on screen
1864 * VT420 uses VGA like hardware and can support any size in
1865 * reasonable range (24..49 AIUI) with no default specified.
1867 compatibility(VT420);
1868 if (esc_nargs == 1 && esc_args[0] > 0) {
1869 request_resize(cols,
1870 def(esc_args[0], cfg.height),
1875 case ANSI('|', '$'):
1876 /* VT340/VT420 sequence DECSCPP
1877 * Set number of columns per page
1878 * Docs imply range is only 80 or 132, but I'll allow any.
1880 compatibility(VT340TEXT);
1881 if (esc_nargs <= 1) {
1882 request_resize(def(esc_args[0], cfg.width),
1887 case 'X': /* write N spaces w/o moving cursor */
1888 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1889 compatibility(ANSIMIN);
1891 int n = def(esc_args[0], 1);
1893 unsigned long *p = cpos;
1894 if (n > cols - curs.x)
1898 check_selection(curs, cursplus);
1901 seen_disp_event = TRUE;
1904 case 'x': /* report terminal characteristics */
1905 compatibility(VT100);
1908 int i = def(esc_args[0], 0);
1909 if (i == 0 || i == 1) {
1910 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1912 ldisc_send(buf, 20);
1916 case ANSI('L', '='):
1917 compatibility(OTHER);
1918 use_bce = (esc_args[0] <= 0);
1919 erase_char = ERASE_CHAR;
1924 (ATTR_FGMASK | ATTR_BGMASK)));
1926 case ANSI('E', '='):
1927 compatibility(OTHER);
1928 blink_is_real = (esc_args[0] >= 1);
1930 case ANSI('p', '"'):
1931 /* Allow the host to make this emulator a 'perfect' VT102.
1932 * This first appeared in the VT220, but we do need to get
1933 * back to PuTTY mode so I won't check it.
1935 * The arg in 40..42,50 are a PuTTY extension.
1936 * The 2nd arg, 8bit vs 7bit is not checked.
1938 * Setting VT102 mode should also change the Fkeys to
1939 * generate PF* codes as a real VT102 has no Fkeys.
1940 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1943 * Note ESC c will NOT change this!
1946 switch (esc_args[0]) {
1948 compatibility_level &= ~TM_VTXXX;
1949 compatibility_level |= TM_VT102;
1952 compatibility_level &= ~TM_VTXXX;
1953 compatibility_level |= TM_VT220;
1957 if (esc_args[0] > 60 && esc_args[0] < 70)
1958 compatibility_level |= TM_VTXXX;
1962 compatibility_level &= TM_VTXXX;
1965 compatibility_level = TM_PUTTY;
1968 compatibility_level = TM_SCOANSI;
1972 compatibility_level = TM_PUTTY;
1978 /* Change the response to CSI c */
1979 if (esc_args[0] == 50) {
1982 strcpy(id_string, "\033[?");
1983 for (i = 1; i < esc_nargs; i++) {
1985 strcat(id_string, ";");
1986 sprintf(lbuf, "%d", esc_args[i]);
1987 strcat(id_string, lbuf);
1989 strcat(id_string, "c");
1992 /* Is this a good idea ?
1993 * Well we should do a soft reset at this point ...
1995 if (!has_compat(VT420) && has_compat(VT100)) {
1997 request_resize(132, 24, 1);
1999 request_resize(80, 24, 1);
2008 case 'P': /* Linux palette sequence */
2009 termstate = SEEN_OSC_P;
2012 case 'R': /* Linux palette reset */
2015 termstate = TOPLEVEL;
2017 case 'W': /* word-set */
2018 termstate = SEEN_OSC_W;
2031 esc_args[0] = 10 * esc_args[0] + c - '0';
2035 * Grotty hack to support xterm and DECterm title
2036 * sequences concurrently.
2038 if (esc_args[0] == 2) {
2042 /* else fall through */
2044 termstate = OSC_STRING;
2050 * This OSC stuff is EVIL. It takes just one character to get into
2051 * sysline mode and it's not initially obvious how to get out.
2052 * So I've added CR and LF as string aborts.
2053 * This shouldn't effect compatibility as I believe embedded
2054 * control characters are supposed to be interpreted (maybe?)
2055 * and they don't display anything useful anyway.
2059 if (c == '\n' || c == '\r') {
2060 termstate = TOPLEVEL;
2061 } else if (c == 0234 || c == '\007') {
2063 * These characters terminate the string; ST and BEL
2064 * terminate the sequence and trigger instant
2065 * processing of it, whereas ESC goes back to SEEN_ESC
2066 * mode unless it is followed by \, in which case it is
2067 * synonymous with ST in the first place.
2070 termstate = TOPLEVEL;
2071 } else if (c == '\033')
2072 termstate = OSC_MAYBE_ST;
2073 else if (osc_strlen < OSC_STR_MAX)
2074 osc_string[osc_strlen++] = c;
2078 int max = (osc_strlen == 0 ? 21 : 16);
2080 if (c >= '0' && c <= '9')
2082 else if (c >= 'A' && c <= 'A' + max - 10)
2084 else if (c >= 'a' && c <= 'a' + max - 10)
2087 termstate = TOPLEVEL;
2090 osc_string[osc_strlen++] = val;
2091 if (osc_strlen >= 7) {
2092 palette_set(osc_string[0],
2093 osc_string[1] * 16 + osc_string[2],
2094 osc_string[3] * 16 + osc_string[4],
2095 osc_string[5] * 16 + osc_string[6]);
2097 termstate = TOPLEVEL;
2113 esc_args[0] = 10 * esc_args[0] + c - '0';
2116 termstate = OSC_STRING;
2121 termstate = TOPLEVEL;
2122 seen_disp_event = TRUE;
2125 move(curs.x, curs.y - 1, 1);
2128 move(curs.x, curs.y + 1, 1);
2131 move(curs.x + 1, curs.y, 1);
2134 move(curs.x - 1, curs.y, 1);
2137 * From the VT100 Manual
2138 * NOTE: The special graphics characters in the VT100
2139 * are different from those in the VT52
2141 * From VT102 manual:
2142 * 137 _ Blank - Same
2143 * 140 ` Reserved - Humm.
2144 * 141 a Solid rectangle - Similar
2145 * 142 b 1/ - Top half of fraction for the
2146 * 143 c 3/ - subscript numbers below.
2149 * 146 f Degrees - Same
2150 * 147 g Plus or minus - Same
2152 * 151 i Ellipsis (dots)
2155 * 154 l Bar at scan 0
2156 * 155 m Bar at scan 1
2157 * 156 n Bar at scan 2
2158 * 157 o Bar at scan 3 - Similar
2159 * 160 p Bar at scan 4 - Similar
2160 * 161 q Bar at scan 5 - Similar
2161 * 162 r Bar at scan 6 - Same
2162 * 163 s Bar at scan 7 - Similar
2177 cset_attr[cset = 0] = ATTR_LINEDRW;
2180 cset_attr[cset = 0] = ATTR_ASCII;
2187 scroll(0, rows - 1, -1, TRUE);
2188 else if (curs.y > 0)
2194 erase_lots(FALSE, FALSE, TRUE);
2198 erase_lots(TRUE, FALSE, TRUE);
2202 /* XXX Print cursor line */
2205 /* XXX Start controller mode */
2208 /* XXX Stop controller mode */
2212 termstate = VT52_Y1;
2215 ldisc_send("\033/Z", 3);
2218 app_keypad_keys = TRUE;
2221 app_keypad_keys = FALSE;
2224 /* XXX This should switch to VT100 mode not current or default
2225 * VT mode. But this will only have effect in a VT220+
2229 blink_is_real = cfg.blinktext;
2233 /* XXX Enter auto print mode */
2236 /* XXX Exit auto print mode */
2239 /* XXX Print screen */
2245 /* compatibility(ATARI) */
2247 erase_lots(FALSE, FALSE, TRUE);
2251 /* compatibility(ATARI) */
2252 if (curs.y <= marg_b)
2253 scroll(curs.y, marg_b, -1, FALSE);
2256 /* compatibility(ATARI) */
2257 if (curs.y <= marg_b)
2258 scroll(curs.y, marg_b, 1, TRUE);
2261 /* compatibility(ATARI) */
2262 termstate = VT52_FG;
2265 /* compatibility(ATARI) */
2266 termstate = VT52_BG;
2269 /* compatibility(ATARI) */
2270 erase_lots(FALSE, TRUE, FALSE);
2274 /* compatibility(ATARI) */
2278 /* compatibility(ATARI) */
2281 /* case 'j': Save cursor position - broken on ST */
2282 /* case 'k': Restore cursor position */
2284 /* compatibility(ATARI) */
2285 erase_lots(TRUE, TRUE, TRUE);
2291 /* compatibility(ATARI) */
2292 erase_lots(TRUE, TRUE, FALSE);
2295 /* compatibility(ATARI) */
2296 curr_attr |= ATTR_REVERSE;
2299 /* compatibility(ATARI) */
2300 curr_attr &= ~ATTR_REVERSE;
2302 case 'v': /* wrap Autowrap on - Wyse style */
2303 /* compatibility(ATARI) */
2306 case 'w': /* Autowrap off */
2307 /* compatibility(ATARI) */
2312 /* compatibility(OTHER) */
2314 curr_attr = ATTR_DEFAULT;
2318 (ATTR_FGMASK | ATTR_BGMASK |
2322 /* compatibility(VI50) */
2323 curr_attr |= ATTR_UNDER;
2326 /* compatibility(VI50) */
2327 curr_attr &= ~ATTR_UNDER;
2330 /* compatibility(VI50) */
2332 curr_attr |= ATTR_BOLD;
2335 /* compatibility(VI50) */
2337 curr_attr &= ~ATTR_BOLD;
2343 termstate = VT52_Y2;
2344 move(curs.x, c - ' ', 0);
2347 termstate = TOPLEVEL;
2348 move(c - ' ', curs.y, 0);
2353 termstate = TOPLEVEL;
2354 curr_attr &= ~ATTR_FGMASK;
2355 curr_attr &= ~ATTR_BOLD;
2356 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2357 if ((c & 0x8) || vt52_bold)
2358 curr_attr |= ATTR_BOLD;
2363 (ATTR_FGMASK | ATTR_BGMASK |
2367 termstate = TOPLEVEL;
2368 curr_attr &= ~ATTR_BGMASK;
2369 curr_attr &= ~ATTR_BLINK;
2370 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2372 /* Note: bold background */
2374 curr_attr |= ATTR_BLINK;
2379 (ATTR_FGMASK | ATTR_BGMASK |
2383 default: break; /* placate gcc warning about enum use */
2385 if (selstate != NO_SELECTION) {
2386 pos cursplus = curs;
2388 check_selection(curs, cursplus);
2396 * Compare two lines to determine whether they are sufficiently
2397 * alike to scroll-optimise one to the other. Return the degree of
2400 static int linecmp(unsigned long *a, unsigned long *b)
2404 for (i = n = 0; i < cols; i++)
2405 n += (*a++ == *b++);
2411 * Given a context, update the window. Out of paranoia, we don't
2412 * allow WM_PAINT responses to do scrolling optimisations.
2414 static void do_paint(Context ctx, int may_optimise)
2416 int i, j, our_curs_y;
2417 unsigned long rv, cursor;
2420 long cursor_background = ERASE_CHAR;
2424 * Check the visual bell state.
2427 ticks = GetTickCount();
2428 if (ticks - vbell_timeout >= 0)
2432 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2435 * screen array, disptop, scrtop,
2437 * cfg.blinkpc, blink_is_real, tblinker,
2438 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2441 /* Has the cursor position or type changed ? */
2444 if (blinker || !cfg.blink_cur)
2445 cursor = TATTR_ACTCURS;
2449 cursor = TATTR_PASCURS;
2451 cursor |= TATTR_RIGHTCURS;
2454 our_curs_y = curs.y - disptop;
2456 if (dispcurs && (curstype != cursor ||
2458 disptext + our_curs_y * (cols + 1) + curs.x)) {
2459 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2460 dispcurs[-1] |= ATTR_INVALID;
2461 if ((*dispcurs & ATTR_WIDE))
2462 dispcurs[1] |= ATTR_INVALID;
2463 *dispcurs |= ATTR_INVALID;
2468 /* The normal screen data */
2469 for (i = 0; i < rows; i++) {
2470 unsigned long *ldata;
2472 int idx, dirty_line, dirty_run;
2473 unsigned long attr = 0;
2474 int updated_line = 0;
2477 int last_run_dirty = 0;
2479 scrpos.y = i + disptop;
2480 ldata = lineptr(scrpos.y);
2481 lattr = (ldata[cols] & LATTR_MODE);
2483 idx = i * (cols + 1);
2484 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2485 disptext[idx + cols] = ldata[cols];
2487 for (j = 0; j < cols; j++, idx++) {
2488 unsigned long tattr, tchar;
2489 unsigned long *d = ldata + j;
2493 tchar = (*d & (CHAR_MASK | CSET_MASK));
2494 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2495 switch (tchar & CSET_MASK) {
2497 tchar = unitab_line[tchar & 0xFF];
2500 tchar = unitab_xterm[tchar & 0xFF];
2503 tattr |= (tchar & CSET_MASK);
2506 /* Video reversing things */
2508 ^ (posle(selstart, scrpos) &&
2509 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2511 /* 'Real' blinking ? */
2512 if (blink_is_real && (tattr & ATTR_BLINK)) {
2513 if (has_focus && tblinker) {
2515 tattr &= ~CSET_MASK;
2518 tattr &= ~ATTR_BLINK;
2521 /* Cursor here ? Save the 'background' */
2522 if (i == our_curs_y && j == curs.x) {
2523 cursor_background = tattr | tchar;
2524 dispcurs = disptext + idx;
2527 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2530 break_run = (tattr != attr || j - start >= sizeof(ch));
2532 /* Special hack for VT100 Linedraw glyphs */
2533 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2534 && tchar <= 0xBD) break_run = TRUE;
2536 if (!dbcs_screenfont && !dirty_line) {
2537 if ((tchar | tattr) == disptext[idx])
2539 else if (!dirty_run && ccount == 1)
2544 if ((dirty_run || last_run_dirty) && ccount > 0) {
2545 do_text(ctx, start, i, ch, ccount, attr, lattr);
2551 if (dbcs_screenfont)
2552 last_run_dirty = dirty_run;
2553 dirty_run = dirty_line;
2556 if ((tchar | tattr) != disptext[idx])
2558 ch[ccount++] = (char) tchar;
2559 disptext[idx] = tchar | tattr;
2561 /* If it's a wide char step along to the next one. */
2562 if (tattr & ATTR_WIDE) {
2566 /* Cursor is here ? Ouch! */
2567 if (i == our_curs_y && j == curs.x) {
2568 cursor_background = *d;
2569 dispcurs = disptext + idx;
2571 if (disptext[idx] != *d)
2577 if (dirty_run && ccount > 0) {
2578 do_text(ctx, start, i, ch, ccount, attr, lattr);
2582 /* Cursor on this line ? (and changed) */
2583 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2584 ch[0] = (char) (cursor_background & CHAR_MASK);
2585 attr = (cursor_background & ATTR_MASK) | cursor;
2586 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2593 * Flick the switch that says if blinking things should be shown or hidden.
2596 void term_blink(int flg)
2598 static long last_blink = 0;
2599 static long last_tblink = 0;
2600 long now, blink_diff;
2602 now = GetTickCount();
2603 blink_diff = now - last_tblink;
2605 /* Make sure the text blinks no more than 2Hz */
2606 if (blink_diff < 0 || blink_diff > 450) {
2608 tblinker = !tblinker;
2617 blink_diff = now - last_blink;
2619 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2620 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2628 * Invalidate the whole screen so it will be repainted in full.
2630 void term_invalidate(void)
2634 for (i = 0; i < rows * (cols + 1); i++)
2635 disptext[i] = ATTR_INVALID;
2639 * Paint the window in response to a WM_PAINT message.
2641 void term_paint(Context ctx, int l, int t, int r, int b)
2643 int i, j, left, top, right, bottom;
2645 left = l / font_width;
2646 right = (r - 1) / font_width;
2647 top = t / font_height;
2648 bottom = (b - 1) / font_height;
2649 for (i = top; i <= bottom && i < rows; i++) {
2650 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2651 for (j = left; j <= right && j < cols; j++)
2652 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2654 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2655 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2658 /* This should happen soon enough, also for some reason it sometimes
2659 * fails to actually do anything when re-sizing ... painting the wrong
2661 do_paint (ctx, FALSE);
2666 * Attempt to scroll the scrollback. The second parameter gives the
2667 * position we want to scroll to; the first is +1 to denote that
2668 * this position is relative to the beginning of the scrollback, -1
2669 * to denote it is relative to the end, and 0 to denote that it is
2670 * relative to the current position.
2672 void term_scroll(int rel, int where)
2674 int sbtop = -count234(scrollback);
2676 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2677 if (disptop < sbtop)
2685 static void clipme(pos top, pos bottom)
2688 wchar_t *wbptr; /* where next char goes within workbuf */
2689 int wblen = 0; /* workbuf len */
2690 int buflen; /* amount of memory allocated to workbuf */
2692 buflen = 5120; /* Default size */
2693 workbuf = smalloc(buflen * sizeof(wchar_t));
2694 wbptr = workbuf; /* start filling here */
2696 while (poslt(top, bottom)) {
2698 unsigned long *ldata = lineptr(top.y);
2704 if (!(ldata[cols] & LATTR_WRAPPED)) {
2705 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2706 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2707 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2708 && poslt(top, nlpos))
2710 if (poslt(nlpos, bottom))
2713 while (poslt(top, bottom) && poslt(top, nlpos)) {
2716 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2718 wchar_t cbuf[16], *p;
2719 int uc = (ldata[top.x] & 0xFFFF);
2722 if (uc == UCSWIDE) {
2727 switch (uc & CSET_MASK) {
2730 uc = unitab_xterm[uc & 0xFF];
2734 uc = unitab_line[uc & 0xFF];
2737 switch (uc & CSET_MASK) {
2739 uc = unitab_font[uc & 0xFF];
2742 uc = unitab_oemcp[uc & 0xFF];
2746 set = (uc & CSET_MASK);
2747 c = (uc & CHAR_MASK);
2751 if (DIRECT_FONT(uc)) {
2752 if (c >= ' ' && c != 0x7F) {
2753 unsigned char buf[4];
2756 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2758 buf[1] = (unsigned char) ldata[top.x + 1];
2759 rv = MultiByteToWideChar(font_codepage,
2760 0, buf, 2, wbuf, 4);
2764 rv = MultiByteToWideChar(font_codepage,
2765 0, buf, 1, wbuf, 4);
2769 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2776 for (p = cbuf; *p; p++) {
2777 /* Enough overhead for trailing NL and nul */
2778 if (wblen >= buflen - 16) {
2781 sizeof(wchar_t) * (buflen += 100));
2782 wbptr = workbuf + wblen;
2791 for (i = 0; i < sel_nl_sz; i++) {
2793 *wbptr++ = sel_nl[i];
2801 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2802 if (buflen > 0) /* indicates we allocated this buffer */
2806 void term_copyall(void)
2809 top.y = -count234(scrollback);
2815 * The wordness array is mainly for deciding the disposition of the US-ASCII
2818 static int wordtype(int uc)
2821 int start, end, ctype;
2822 } *wptr, ucs_words[] = {
2828 0x037e, 0x037e, 1}, /* Greek question mark */
2830 0x0387, 0x0387, 1}, /* Greek ano teleia */
2832 0x055a, 0x055f, 1}, /* Armenian punctuation */
2834 0x0589, 0x0589, 1}, /* Armenian full stop */
2836 0x0700, 0x070d, 1}, /* Syriac punctuation */
2838 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2840 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2842 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2844 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2846 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2848 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2850 0x2000, 0x200a, 0}, /* Various spaces */
2852 0x2070, 0x207f, 2}, /* superscript */
2854 0x2080, 0x208f, 2}, /* subscript */
2856 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2858 0x3000, 0x3000, 0}, /* ideographic space */
2860 0x3001, 0x3020, 1}, /* ideographic punctuation */
2862 0x303f, 0x309f, 3}, /* Hiragana */
2864 0x30a0, 0x30ff, 3}, /* Katakana */
2866 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2868 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2870 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2872 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2874 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2876 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2878 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2880 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2882 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2887 uc &= (CSET_MASK | CHAR_MASK);
2889 switch (uc & CSET_MASK) {
2891 uc = unitab_xterm[uc & 0xFF];
2894 uc = unitab_line[uc & 0xFF];
2897 switch (uc & CSET_MASK) {
2899 uc = unitab_font[uc & 0xFF];
2902 uc = unitab_oemcp[uc & 0xFF];
2907 return wordness[uc];
2909 for (wptr = ucs_words; wptr->start; wptr++) {
2910 if (uc >= wptr->start && uc <= wptr->end)
2918 * Spread the selection outwards according to the selection mode.
2920 static pos sel_spread_half(pos p, int dir)
2922 unsigned long *ldata;
2925 ldata = lineptr(p.y);
2930 * In this mode, every character is a separate unit, except
2931 * for runs of spaces at the end of a non-wrapping line.
2933 if (!(ldata[cols] & LATTR_WRAPPED)) {
2934 unsigned long *q = ldata + cols;
2935 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2937 if (q == ldata + cols)
2939 if (p.x >= q - ldata)
2940 p.x = (dir == -1 ? q - ldata : cols - 1);
2945 * In this mode, the units are maximal runs of characters
2946 * whose `wordness' has the same value.
2948 wvalue = wordtype(ldata[p.x]);
2950 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2953 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2959 * In this mode, every line is a unit.
2961 p.x = (dir == -1 ? 0 : cols - 1);
2967 static void sel_spread(void)
2969 selstart = sel_spread_half(selstart, -1);
2971 selend = sel_spread_half(selend, +1);
2975 void term_do_paste(void)
2980 get_clip(&data, &len);
2985 sfree(paste_buffer);
2986 paste_pos = paste_hold = paste_len = 0;
2987 paste_buffer = smalloc(len * sizeof(wchar_t));
2990 while (p < data + len) {
2991 while (p < data + len &&
2992 !(p <= data + len - sel_nl_sz &&
2993 !memcmp(p, sel_nl, sizeof(sel_nl))))
2998 for (i = 0; i < p - q; i++) {
2999 paste_buffer[paste_len++] = q[i];
3003 if (p <= data + len - sel_nl_sz &&
3004 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3005 paste_buffer[paste_len++] = '\r';
3011 /* Assume a small paste will be OK in one go. */
3012 if (paste_len < 256) {
3013 luni_send(paste_buffer, paste_len);
3015 sfree(paste_buffer);
3017 paste_pos = paste_hold = paste_len = 0;
3020 get_clip(NULL, NULL);
3023 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3024 int shift, int ctrl)
3027 unsigned long *ldata;
3043 selpoint.y = y + disptop;
3045 ldata = lineptr(selpoint.y);
3046 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3050 int encstate = 0, r, c;
3052 static int is_down = 0;
3056 encstate = 0x20; /* left button down */
3067 case MBT_WHEEL_DOWN:
3070 default: break; /* placate gcc warning about enum use */
3074 if (xterm_mouse == 1)
3087 default: break; /* placate gcc warning about enum use */
3096 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3097 ldisc_send(abuf, 6);
3101 b = translate_button(b);
3103 if (b == MBT_SELECT && a == MA_CLICK) {
3105 selstate = ABOUT_TO;
3106 selanchor = selpoint;
3108 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3110 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3111 selstate = DRAGGING;
3112 selstart = selanchor = selpoint;
3116 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3117 (b == MBT_EXTEND && a != MA_RELEASE)) {
3118 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3120 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3121 if (posdiff(selpoint, selstart) <
3122 posdiff(selend, selstart) / 2) {
3126 selanchor = selstart;
3128 selstate = DRAGGING;
3130 if (selstate != ABOUT_TO && selstate != DRAGGING)
3131 selanchor = selpoint;
3132 selstate = DRAGGING;
3133 if (poslt(selpoint, selanchor)) {
3134 selstart = selpoint;
3138 selstart = selanchor;
3143 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3144 if (selstate == DRAGGING) {
3146 * We've completed a selection. We now transfer the
3147 * data to the clipboard.
3149 clipme(selstart, selend);
3150 selstate = SELECTED;
3152 selstate = NO_SELECTION;
3153 } else if (b == MBT_PASTE
3154 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3165 sfree(paste_buffer);
3172 static long last_paste = 0;
3173 long now, paste_diff;
3178 /* Don't wait forever to paste */
3180 now = GetTickCount();
3181 paste_diff = now - last_paste;
3182 if (paste_diff >= 0 && paste_diff < 450)
3187 while (paste_pos < paste_len) {
3189 while (n + paste_pos < paste_len) {
3190 if (paste_buffer[paste_pos + n++] == '\r')
3193 luni_send(paste_buffer + paste_pos, n);
3196 if (paste_pos < paste_len) {
3201 sfree(paste_buffer);
3206 static void deselect(void)
3208 selstate = NO_SELECTION;
3209 selstart.x = selstart.y = selend.x = selend.y = 0;
3212 void term_deselect(void)
3218 int term_ldisc(int option)
3220 if (option == LD_ECHO)
3221 return term_echoing;
3222 if (option == LD_EDIT)
3223 return term_editing;
3228 * from_backend(), to get data from the backend for the terminal.
3230 void from_backend(int is_stderr, char *data, int len)
3233 if (inbuf_head >= INBUF_SIZE)
3235 inbuf[inbuf_head++] = *data++;
3240 * Log session traffic.
3242 void logtraffic(unsigned char c, int logmode)
3244 if (cfg.logtype > 0) {
3245 if (cfg.logtype == logmode) {
3246 /* deferred open file from pgm start? */
3255 /* open log file append/overwrite mode */
3265 sprintf(writemod, "wb"); /* default to rewrite */
3266 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3270 i = askappend(cfg.logfilename);
3272 writemod[0] = 'a'; /* set append mode */
3273 else if (i == 0) { /* cancelled */
3275 cfg.logtype = 0; /* disable logging */
3280 lgfp = fopen(cfg.logfilename, writemod);
3281 if (lgfp) { /* enter into event log */
3282 sprintf(buf, "%s session log (%s mode) to file : ",
3283 (writemod[0] == 'a') ? "Appending" : "Writing new",
3284 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3285 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3286 /* Make sure we do not exceed the output buffer size */
3287 strncat(buf, cfg.logfilename, 128);
3288 buf[strlen(buf)] = '\0';
3291 /* --- write header line iinto log file */
3292 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3295 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3297 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3301 void logfclose(void)