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 bufchain inbuf; /* terminal input buffer */
90 static pos curs; /* cursor */
91 static pos savecurs; /* saved cursor position */
92 static int marg_t, marg_b; /* scroll margins */
93 static int dec_om; /* DEC origin mode flag */
94 static int wrap, wrapnext; /* wrap flags */
95 static int insert; /* insert-mode flag */
96 static int cset; /* 0 or 1: which char set */
97 static int save_cset, save_csattr; /* saved with cursor position */
98 static int save_utf; /* saved with cursor position */
99 static int rvideo; /* global reverse video flag */
100 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
101 static int cursor_on; /* cursor enabled flag */
102 static int reset_132; /* Flag ESC c resets to 80 cols */
103 static int use_bce; /* Use Background coloured erase */
104 static int blinker; /* When blinking is the cursor on ? */
105 static int tblinker; /* When the blinking text is on */
106 static int blink_is_real; /* Actually blink blinking text */
107 static int term_echoing; /* Does terminal want local echo? */
108 static int term_editing; /* Does terminal want local edit? */
109 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
110 static int vt52_bold; /* Force bold on non-bold colours */
111 static int utf_state; /* Is there a pending UTF-8 character */
112 static int utf_char; /* and what is it so far. */
113 static int utf_size; /* The size of the UTF character. */
115 static int xterm_mouse; /* send mouse messages to app */
117 static unsigned long cset_attr[2];
120 * Saved settings on the alternate screen.
122 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
123 static int alt_t, alt_b;
124 static int alt_which;
126 #define ARGS_MAX 32 /* max # of esc sequence arguments */
127 #define ARG_DEFAULT 0 /* if an arg isn't specified */
128 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
129 static int esc_args[ARGS_MAX];
130 static int esc_nargs;
131 static int esc_query;
132 #define ANSI(x,y) ((x)+((y)<<8))
133 #define ANSI_QUE(x) ANSI(x,TRUE)
135 #define OSC_STR_MAX 2048
136 static int osc_strlen;
137 static char osc_string[OSC_STR_MAX + 1];
140 static char id_string[1024] = "\033[?6c";
142 static unsigned char *tabs;
154 OSC_STRING, OSC_MAYBE_ST,
163 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
166 SM_CHAR, SM_WORD, SM_LINE
168 static pos selstart, selend, selanchor;
170 static short wordness[256] = {
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
173 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
174 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
175 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
176 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
177 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
178 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
179 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
181 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
182 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
183 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
184 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
185 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
186 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
189 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
190 static wchar_t sel_nl[] = SEL_NL;
191 static wchar_t *paste_buffer = 0;
192 static int paste_len, paste_pos, paste_hold;
195 * Internal prototypes.
197 static void do_paint(Context, int);
198 static void erase_lots(int, int, int);
199 static void swap_screen(int);
200 static void update_sbar(void);
201 static void deselect(void);
202 /* log session to file stuff ... */
203 static FILE *lgfp = NULL;
204 static void logtraffic(unsigned char c, int logmode);
205 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
208 * Resize a line to make it `cols' columns wide.
210 unsigned long *resizeline(unsigned long *line, int cols)
213 unsigned long lineattrs;
215 if (line[0] != (unsigned long)cols) {
217 * This line is the wrong length, which probably means it
218 * hasn't been accessed since a resize. Resize it now.
221 lineattrs = line[oldlen + 1];
222 line = srealloc(line, TSIZE * (2 + cols));
224 for (i = oldlen; i < cols; i++)
225 line[i + 1] = ERASE_CHAR;
226 line[cols + 1] = lineattrs & LATTR_MODE;
233 * Retrieve a line of the screen or of the scrollback, according to
234 * whether the y coordinate is non-negative or negative
237 unsigned long *lineptr(int y, int lineno)
239 unsigned long *line, *newline;
247 whichtree = scrollback;
248 treeindex = y + count234(scrollback);
250 line = index234(whichtree, treeindex);
252 /* We assume that we don't screw up and retrieve something out of range. */
253 assert(line != NULL);
255 newline = resizeline(line, cols);
256 if (newline != line) {
257 delpos234(whichtree, treeindex);
258 addpos234(whichtree, newline, treeindex);
265 #define lineptr(x) lineptr(x,__LINE__)
267 * Set up power-on settings for the terminal.
269 static void power_on(void)
271 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
274 alt_b = marg_b = rows - 1;
279 for (i = 0; i < cols; i++)
280 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
282 alt_om = dec_om = cfg.dec_om;
283 alt_wnext = wrapnext = alt_ins = insert = FALSE;
284 alt_wrap = wrap = cfg.wrap_mode;
287 alt_sco_acs = sco_acs = 0;
288 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
293 save_attr = curr_attr = ATTR_DEFAULT;
294 term_editing = term_echoing = FALSE;
295 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
296 app_cursor_keys = cfg.app_cursor;
297 app_keypad_keys = cfg.app_keypad;
299 blink_is_real = cfg.blinktext;
300 erase_char = ERASE_CHAR;
304 for (i = 0; i < 256; i++)
305 wordness[i] = cfg.wordness[i];
309 erase_lots(FALSE, TRUE, TRUE);
311 erase_lots(FALSE, TRUE, TRUE);
316 * Force a screen update.
318 void term_update(void)
323 int need_sbar_update = seen_disp_event;
324 if ((seen_key_event && (cfg.scroll_on_key)) ||
325 (seen_disp_event && (cfg.scroll_on_disp))) {
326 disptop = 0; /* return to main screen */
327 seen_disp_event = seen_key_event = 0;
328 need_sbar_update = TRUE;
330 if (need_sbar_update)
333 sys_cursor(curs.x, curs.y - disptop);
339 * Same as power_on(), but an external function.
341 void term_pwron(void)
351 * Clear the scrollback.
353 void term_clrsb(void)
357 while ((line = delpos234(scrollback, 0)) != NULL) {
364 * Initialise the terminal.
368 screen = alt_screen = scrollback = NULL;
370 disptext = dispcurs = NULL;
375 beephead = beeptail = NULL;
378 beep_overloaded = FALSE;
382 * Set up the terminal for a given size.
384 void term_size(int newrows, int newcols, int newsavelines)
387 unsigned long *newdisp, *line;
390 int save_alt_which = alt_which;
392 if (newrows == rows && newcols == cols && newsavelines == savelines)
393 return; /* nothing to do */
399 alt_b = marg_b = newrows - 1;
402 scrollback = newtree234(NULL);
403 screen = newtree234(NULL);
408 * Resize the screen and scrollback. We only need to shift
409 * lines around within our data structures, because lineptr()
410 * will take care of resizing each individual line if
413 * - If the new screen and the old screen differ in length, we
414 * must shunt some lines in from the scrollback or out to
417 * - If doing that fails to provide us with enough material to
418 * fill the new screen (i.e. the number of rows needed in
419 * the new screen exceeds the total number in the previous
420 * screen+scrollback), we must invent some blank lines to
423 * - Then, if the new scrollback length is less than the
424 * amount of scrollback we actually have, we must throw some
427 sblen = count234(scrollback);
428 /* Do this loop to expand the screen if newrows > rows */
429 for (i = rows; i < newrows; i++) {
431 line = delpos234(scrollback, --sblen);
433 line = smalloc(TSIZE * (newcols + 2));
435 for (j = 0; j <= newcols; j++)
436 line[j + 1] = ERASE_CHAR;
438 addpos234(screen, line, 0);
440 /* Do this loop to shrink the screen if newrows < rows */
441 for (i = newrows; i < rows; i++) {
442 line = delpos234(screen, 0);
443 addpos234(scrollback, line, sblen++);
445 assert(count234(screen) == newrows);
446 while (sblen > newsavelines) {
447 line = delpos234(scrollback, 0);
451 assert(count234(scrollback) <= newsavelines);
454 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
455 for (i = 0; i < newrows * (newcols + 1); i++)
456 newdisp[i] = ATTR_INVALID;
461 newalt = newtree234(NULL);
462 for (i = 0; i < newrows; i++) {
463 line = smalloc(TSIZE * (newcols + 2));
465 for (j = 0; j <= newcols; j++)
466 line[j + 1] = erase_char;
467 addpos234(newalt, line, i);
470 while (NULL != (line = delpos234(alt_screen, 0)))
472 freetree234(alt_screen);
476 tabs = srealloc(tabs, newcols * sizeof(*tabs));
479 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
480 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
484 curs.y += newrows - rows;
487 if (curs.y >= newrows)
488 curs.y = newrows - 1;
489 if (curs.x >= newcols)
490 curs.x = newcols - 1;
492 wrapnext = alt_wnext = FALSE;
496 savelines = newsavelines;
499 swap_screen(save_alt_which);
509 static void swap_screen(int which)
514 if (which == alt_which)
541 wrapnext = alt_wnext;
553 sco_acs = alt_sco_acs;
560 * Update the scroll bar.
562 static void update_sbar(void)
566 nscroll = count234(scrollback);
568 set_sbar(nscroll + rows, nscroll + disptop, rows);
572 * Check whether the region bounded by the two pointers intersects
573 * the scroll region, and de-select the on-screen selection if so.
575 static void check_selection(pos from, pos to)
577 if (poslt(from, selend) && poslt(selstart, to))
582 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
583 * for backward.) `sb' is TRUE if the scrolling is permitted to
584 * affect the scrollback buffer.
586 * NB this function invalidates all pointers into lines of the
587 * screen data structures. In particular, you MUST call fix_cpos
588 * after calling scroll() and before doing anything else that
589 * uses the cpos shortcut pointer.
591 static void scroll(int topline, int botline, int lines, int sb)
593 unsigned long *line, *line2;
596 if (topline != 0 || alt_which != 0)
601 line = delpos234(screen, botline);
602 line = resizeline(line, cols);
603 for (i = 0; i < cols; i++)
604 line[i + 1] = erase_char;
606 addpos234(screen, line, topline);
608 if (selstart.y >= topline && selstart.y <= botline) {
610 if (selstart.y > botline) {
611 selstart.y = botline;
615 if (selend.y >= topline && selend.y <= botline) {
617 if (selend.y > botline) {
627 line = delpos234(screen, topline);
628 if (sb && savelines > 0) {
629 int sblen = count234(scrollback);
631 * We must add this line to the scrollback. We'll
632 * remove a line from the top of the scrollback to
633 * replace it, or allocate a new one if the
634 * scrollback isn't full.
636 if (sblen == savelines) {
637 sblen--, line2 = delpos234(scrollback, 0);
639 line2 = smalloc(TSIZE * (cols + 2));
642 addpos234(scrollback, line, sblen);
646 * If the user is currently looking at part of the
647 * scrollback, and they haven't enabled any options
648 * that are going to reset the scrollback as a
649 * result of this movement, then the chances are
650 * they'd like to keep looking at the same line. So
651 * we move their viewpoint at the same rate as the
652 * scroll, at least until their viewpoint hits the
653 * top end of the scrollback buffer, at which point
654 * we don't have the choice any more.
656 * Thanks to Jan Holmen Holsten for the idea and
657 * initial implementation.
659 if (disptop > -savelines && disptop < 0)
662 line = resizeline(line, cols);
663 for (i = 0; i < cols; i++)
664 line[i + 1] = erase_char;
666 addpos234(screen, line, botline);
669 * If the selection endpoints move into the scrollback,
670 * we keep them moving until they hit the top. However,
671 * of course, if the line _hasn't_ moved into the
672 * scrollback then we don't do this, and cut them off
673 * at the top of the scroll region.
675 * This applies to selstart and selend (for an existing
676 * selection), and also selanchor (for one being
677 * selected as we speak).
679 seltop = sb ? -savelines : 0;
681 if (selstart.y >= seltop && selstart.y <= botline) {
683 if (selstart.y < seltop) {
688 if (selend.y >= seltop && selend.y <= botline) {
690 if (selend.y < seltop) {
695 if (selanchor.y >= seltop && selanchor.y <= botline) {
697 if (selanchor.y < seltop) {
698 selanchor.y = seltop;
709 * Move the cursor to a given position, clipping at boundaries. We
710 * may or may not want to clip at the scroll margin: marg_clip is 0
711 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
712 * even _being_ outside the margins.
714 static void move(int x, int y, int marg_clip)
721 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
723 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
737 * Save or restore the cursor and SGR mode.
739 static void save_cursor(int save)
743 save_attr = curr_attr;
746 save_csattr = cset_attr[cset];
747 save_sco_acs = sco_acs;
750 /* Make sure the window hasn't shrunk since the save */
756 curr_attr = save_attr;
759 cset_attr[cset] = save_csattr;
760 sco_acs = save_sco_acs;
763 erase_char = (' ' | ATTR_ASCII |
764 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
769 * Erase a large portion of the screen: the whole screen, or the
770 * whole line, or parts thereof.
772 static void erase_lots(int line_only, int from_begin, int to_end)
776 unsigned long *ldata;
798 check_selection(start, end);
800 /* Clear screen also forces a full window redraw, just in case. */
801 if (start.y == 0 && start.x == 0 && end.y == rows)
804 ldata = lineptr(start.y);
805 while (poslt(start, end)) {
806 if (start.x == cols && !erase_lattr)
807 ldata[start.x] &= ~LATTR_WRAPPED;
809 ldata[start.x] = erase_char;
810 if (incpos(start) && start.y < rows)
811 ldata = lineptr(start.y);
816 * Insert or delete characters within the current line. n is +ve if
817 * insertion is desired, and -ve for deletion.
819 static void insch(int n)
821 int dir = (n < 0 ? -1 : +1);
824 unsigned long *ldata;
826 n = (n < 0 ? -n : n);
827 if (n > cols - curs.x)
829 m = cols - curs.x - n;
831 cursplus.x = curs.x + n;
832 check_selection(curs, cursplus);
833 ldata = lineptr(curs.y);
835 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
837 ldata[curs.x + m++] = erase_char;
839 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
841 ldata[curs.x + n] = erase_char;
846 * Toggle terminal mode `mode' to state `state'. (`query' indicates
847 * whether the mode is a DEC private one or a normal one.)
849 static void toggle_mode(int mode, int query, int state)
855 case 1: /* application cursor keys */
856 app_cursor_keys = state;
858 case 2: /* VT52 mode */
861 blink_is_real = FALSE;
864 blink_is_real = cfg.blinktext;
867 case 3: /* 80/132 columns */
869 request_resize(state ? 132 : 80, rows);
872 case 5: /* reverse video */
874 * Toggle reverse video. If we receive an OFF within the
875 * visual bell timeout period after an ON, we trigger an
876 * effective visual bell, so that ESC[?5hESC[?5l will
877 * always be an actually _visible_ visual bell.
879 ticks = GetTickCount();
880 if (rvideo && !state && /* we're turning it off */
881 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
882 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
883 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
884 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
885 } else if (!rvideo && state) {
886 /* This is an ON, so we notice the time and save it. */
887 rvbell_timeout = ticks + VBELL_TIMEOUT;
890 seen_disp_event = TRUE;
894 case 6: /* DEC origin mode */
897 case 7: /* auto wrap */
900 case 8: /* auto key repeat */
903 case 10: /* set local edit mode */
904 term_editing = state;
905 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
907 case 25: /* enable/disable cursor */
908 compatibility2(OTHER, VT220);
910 seen_disp_event = TRUE;
912 case 47: /* alternate screen */
913 compatibility(OTHER);
918 case 1000: /* xterm mouse 1 */
919 xterm_mouse = state ? 1 : 0;
920 set_raw_mouse_mode(state);
922 case 1002: /* xterm mouse 2 */
923 xterm_mouse = state ? 2 : 0;
924 set_raw_mouse_mode(state);
928 case 4: /* set insert mode */
929 compatibility(VT102);
932 case 12: /* set echo mode */
933 term_echoing = !state;
934 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
936 case 20: /* Return sends ... */
937 cr_lf_return = state;
939 case 34: /* Make cursor BIG */
940 compatibility2(OTHER, VT220);
946 * Process an OSC sequence: set window title or icon name.
948 static void do_osc(void)
952 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
954 osc_string[osc_strlen] = '\0';
955 switch (esc_args[0]) {
958 set_icon(osc_string);
959 if (esc_args[0] == 1)
961 /* fall through: parameter 0 means set both */
964 set_title(osc_string);
971 * Remove everything currently in `inbuf' and stick it up on the
972 * in-memory display. There's a big state machine in here to
973 * process escape sequences...
978 unsigned char localbuf[256], *chars;
983 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
987 bufchain_prefix(&inbuf, &ret, &nchars);
988 if (nchars > sizeof(localbuf))
989 nchars = sizeof(localbuf);
990 memcpy(localbuf, ret, nchars);
991 bufchain_consume(&inbuf, nchars);
993 assert(chars != NULL);
999 * Optionally log the session traffic to a file. Useful for
1000 * debugging and possibly also useful for actual logging.
1002 if (cfg.logtype == LGTYP_DEBUG)
1003 logtraffic((unsigned char) &c, LGTYP_DEBUG);
1009 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1010 * be able to display 8-bit characters, but I'll let that go 'cause
1014 /* First see about all those translations. */
1015 if (termstate == TOPLEVEL) {
1017 switch (utf_state) {
1020 /* UTF-8 must be stateless so we ignore iso2022. */
1021 if (unitab_ctrl[c] != 0xFF)
1023 else c = ((unsigned char)c) | ATTR_ASCII;
1025 } else if ((c & 0xe0) == 0xc0) {
1026 utf_size = utf_state = 1;
1027 utf_char = (c & 0x1f);
1028 } else if ((c & 0xf0) == 0xe0) {
1029 utf_size = utf_state = 2;
1030 utf_char = (c & 0x0f);
1031 } else if ((c & 0xf8) == 0xf0) {
1032 utf_size = utf_state = 3;
1033 utf_char = (c & 0x07);
1034 } else if ((c & 0xfc) == 0xf8) {
1035 utf_size = utf_state = 4;
1036 utf_char = (c & 0x03);
1037 } else if ((c & 0xfe) == 0xfc) {
1038 utf_size = utf_state = 5;
1039 utf_char = (c & 0x01);
1050 if ((c & 0xC0) != 0x80) {
1056 utf_char = (utf_char << 6) | (c & 0x3f);
1062 /* Is somebody trying to be evil! */
1064 (c < 0x800 && utf_size >= 2) ||
1065 (c < 0x10000 && utf_size >= 3) ||
1066 (c < 0x200000 && utf_size >= 4) ||
1067 (c < 0x4000000 && utf_size >= 5))
1070 /* Unicode line separator and paragraph separator are CR-LF */
1071 if (c == 0x2028 || c == 0x2029)
1074 /* High controls are probably a Baaad idea too. */
1078 /* The UTF-16 surrogates are not nice either. */
1079 /* The standard give the option of decoding these:
1080 * I don't want to! */
1081 if (c >= 0xD800 && c < 0xE000)
1084 /* ISO 10646 characters now limited to UTF-16 range. */
1088 /* This is currently a TagPhobic application.. */
1089 if (c >= 0xE0000 && c <= 0xE007F)
1092 /* U+FEFF is best seen as a null. */
1095 /* But U+FFFE is an error. */
1096 if (c == 0xFFFE || c == 0xFFFF)
1099 /* Oops this is a 16bit implementation */
1104 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1106 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1108 if (sco_acs == 2) c ^= 0x80;
1111 switch (cset_attr[cset]) {
1113 * Linedraw characters are different from 'ESC ( B'
1114 * only for a small range. For ones outside that
1115 * range, make sure we use the same font as well as
1116 * the same encoding.
1119 if (unitab_ctrl[c] != 0xFF)
1122 c = ((unsigned char) c) | ATTR_LINEDRW;
1126 /* If UK-ASCII, make the '#' a LineDraw Pound */
1128 c = '}' | ATTR_LINEDRW;
1131 /*FALLTHROUGH*/ case ATTR_ASCII:
1132 if (unitab_ctrl[c] != 0xFF)
1135 c = ((unsigned char) c) | ATTR_ASCII;
1138 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1144 /* How about C1 controls ? */
1145 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1146 has_compat(VT220)) {
1147 termstate = SEEN_ESC;
1149 c = '@' + (c & 0x1F);
1152 /* Or the GL control. */
1153 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1154 if (curs.x && !wrapnext)
1158 *cpos = (' ' | curr_attr | ATTR_ASCII);
1160 /* Or normal C0 controls. */
1161 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1163 case '\005': /* terminal type query */
1164 /* Strictly speaking this is VT100 but a VT100 defaults to
1165 * no response. Other terminals respond at their option.
1167 * Don't put a CR in the default string as this tends to
1168 * upset some weird software.
1170 * An xterm returns "xterm" (5 characters)
1172 compatibility(ANSIMIN);
1174 char abuf[256], *s, *d;
1176 for (s = cfg.answerback, d = abuf; *s; s++) {
1178 if (*s >= 'a' && *s <= 'z')
1179 *d++ = (*s - ('a' - 1));
1180 else if ((*s >= '@' && *s <= '_') ||
1181 *s == '?' || (*s & 0x80))
1186 } else if (*s == '^') {
1191 lpage_send(CP_ACP, abuf, d - abuf, 0);
1196 struct beeptime *newbeep;
1199 ticks = GetTickCount();
1201 if (!beep_overloaded) {
1202 newbeep = smalloc(sizeof(struct beeptime));
1203 newbeep->ticks = ticks;
1204 newbeep->next = NULL;
1208 beeptail->next = newbeep;
1214 * Throw out any beeps that happened more than
1218 beephead->ticks < ticks - cfg.bellovl_t) {
1219 struct beeptime *tmp = beephead;
1220 beephead = tmp->next;
1227 if (cfg.bellovl && beep_overloaded &&
1228 ticks - lastbeep >= cfg.bellovl_s) {
1230 * If we're currently overloaded and the
1231 * last beep was more than s seconds ago,
1232 * leave overload mode.
1234 beep_overloaded = FALSE;
1235 } else if (cfg.bellovl && !beep_overloaded &&
1236 nbeeps >= cfg.bellovl_n) {
1238 * Now, if we have n or more beeps
1239 * remaining in the queue, go into overload
1242 beep_overloaded = TRUE;
1247 * Perform an actual beep if we're not overloaded.
1249 if (!cfg.bellovl || !beep_overloaded) {
1251 if (cfg.beep == BELL_VISUAL) {
1253 vbell_timeout = ticks + VBELL_TIMEOUT;
1261 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1262 else if (curs.x == 0 && curs.y > 0)
1263 curs.x = cols - 1, curs.y--;
1269 seen_disp_event = TRUE;
1272 compatibility(VT100);
1276 compatibility(VT100);
1281 termstate = VT52_ESC;
1283 compatibility(ANSIMIN);
1284 termstate = SEEN_ESC;
1292 seen_disp_event = TRUE;
1294 logtraffic((unsigned char) c, LGTYP_ASCII);
1297 if (has_compat(SCOANSI)) {
1299 erase_lots(FALSE, FALSE, TRUE);
1302 seen_disp_event = 1;
1306 compatibility(VT100);
1308 if (curs.y == marg_b)
1309 scroll(marg_t, marg_b, 1, TRUE);
1310 else if (curs.y < rows - 1)
1316 seen_disp_event = 1;
1318 logtraffic((unsigned char) c, LGTYP_ASCII);
1322 pos old_curs = curs;
1323 unsigned long *ldata = lineptr(curs.y);
1327 } while (curs.x < cols - 1 && !tabs[curs.x]);
1329 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1330 if (curs.x >= cols / 2)
1331 curs.x = cols / 2 - 1;
1338 check_selection(old_curs, curs);
1340 seen_disp_event = TRUE;
1344 switch (termstate) {
1346 /* Only graphic characters get this far, ctrls are stripped above */
1347 if (wrapnext && wrap) {
1348 cpos[1] |= LATTR_WRAPPED;
1349 if (curs.y == marg_b)
1350 scroll(marg_t, marg_b, 1, TRUE);
1351 else if (curs.y < rows - 1)
1359 if (selstate != NO_SELECTION) {
1360 pos cursplus = curs;
1362 check_selection(curs, cursplus);
1364 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1365 logtraffic((unsigned char) c, LGTYP_ASCII);
1367 extern int wcwidth(wchar_t ucs);
1372 width = wcwidth((wchar_t) c);
1375 *cpos++ = c | curr_attr;
1376 if (++curs.x == cols) {
1377 *cpos |= LATTR_WRAPPED;
1378 if (curs.y == marg_b)
1379 scroll(marg_t, marg_b, 1, TRUE);
1380 else if (curs.y < rows - 1)
1385 *cpos++ = UCSWIDE | curr_attr;
1388 *cpos++ = c | curr_attr;
1395 if (curs.x == cols) {
1399 if (wrap && vt52_mode) {
1400 cpos[1] |= LATTR_WRAPPED;
1401 if (curs.y == marg_b)
1402 scroll(marg_t, marg_b, 1, TRUE);
1403 else if (curs.y < rows - 1)
1410 seen_disp_event = 1;
1415 * This state is virtually identical to SEEN_ESC, with the
1416 * exception that we have an OSC sequence in the pipeline,
1417 * and _if_ we see a backslash, we process it.
1421 termstate = TOPLEVEL;
1424 /* else fall through */
1426 if (c >= ' ' && c <= '/') {
1433 termstate = TOPLEVEL;
1434 switch (ANSI(c, esc_query)) {
1435 case '[': /* enter CSI mode */
1436 termstate = SEEN_CSI;
1438 esc_args[0] = ARG_DEFAULT;
1441 case ']': /* xterm escape sequences */
1442 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1443 compatibility(OTHER);
1444 termstate = SEEN_OSC;
1447 case '7': /* save cursor */
1448 compatibility(VT100);
1451 case '8': /* restore cursor */
1452 compatibility(VT100);
1454 seen_disp_event = TRUE;
1457 compatibility(VT100);
1458 app_keypad_keys = TRUE;
1461 compatibility(VT100);
1462 app_keypad_keys = FALSE;
1464 case 'D': /* exactly equivalent to LF */
1465 compatibility(VT100);
1466 if (curs.y == marg_b)
1467 scroll(marg_t, marg_b, 1, TRUE);
1468 else if (curs.y < rows - 1)
1472 seen_disp_event = TRUE;
1474 case 'E': /* exactly equivalent to CR-LF */
1475 compatibility(VT100);
1477 if (curs.y == marg_b)
1478 scroll(marg_t, marg_b, 1, TRUE);
1479 else if (curs.y < rows - 1)
1483 seen_disp_event = TRUE;
1485 case 'M': /* reverse index - backwards LF */
1486 compatibility(VT100);
1487 if (curs.y == marg_t)
1488 scroll(marg_t, marg_b, -1, TRUE);
1489 else if (curs.y > 0)
1493 seen_disp_event = TRUE;
1495 case 'Z': /* terminal type query */
1496 compatibility(VT100);
1497 ldisc_send(id_string, strlen(id_string), 0);
1499 case 'c': /* restore power-on settings */
1500 compatibility(VT100);
1503 request_resize(80, rows);
1508 seen_disp_event = TRUE;
1510 case 'H': /* set a tab */
1511 compatibility(VT100);
1512 tabs[curs.x] = TRUE;
1515 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1516 compatibility(VT100);
1518 unsigned long *ldata;
1522 for (i = 0; i < rows; i++) {
1524 for (j = 0; j < cols; j++)
1525 ldata[j] = ATTR_DEFAULT | 'E';
1529 seen_disp_event = TRUE;
1530 scrtop.x = scrtop.y = 0;
1533 check_selection(scrtop, scrbot);
1537 case ANSI('3', '#'):
1538 case ANSI('4', '#'):
1539 case ANSI('5', '#'):
1540 case ANSI('6', '#'):
1541 compatibility(VT100);
1543 unsigned long nlattr;
1544 unsigned long *ldata;
1545 switch (ANSI(c, esc_query)) {
1546 case ANSI('3', '#'):
1549 case ANSI('4', '#'):
1552 case ANSI('5', '#'):
1553 nlattr = LATTR_NORM;
1555 default: /* spiritually case ANSI('6', '#'): */
1556 nlattr = LATTR_WIDE;
1559 ldata = lineptr(curs.y);
1560 ldata[cols] &= ~LATTR_MODE;
1561 ldata[cols] |= nlattr;
1565 case ANSI('A', '('):
1566 compatibility(VT100);
1567 cset_attr[0] = ATTR_GBCHR;
1569 case ANSI('B', '('):
1570 compatibility(VT100);
1571 cset_attr[0] = ATTR_ASCII;
1573 case ANSI('0', '('):
1574 compatibility(VT100);
1575 cset_attr[0] = ATTR_LINEDRW;
1577 case ANSI('U', '('):
1578 compatibility(OTHER);
1579 cset_attr[0] = ATTR_SCOACS;
1582 case ANSI('A', ')'):
1583 compatibility(VT100);
1584 cset_attr[1] = ATTR_GBCHR;
1586 case ANSI('B', ')'):
1587 compatibility(VT100);
1588 cset_attr[1] = ATTR_ASCII;
1590 case ANSI('0', ')'):
1591 compatibility(VT100);
1592 cset_attr[1] = ATTR_LINEDRW;
1594 case ANSI('U', ')'):
1595 compatibility(OTHER);
1596 cset_attr[1] = ATTR_SCOACS;
1599 case ANSI('8', '%'): /* Old Linux code */
1600 case ANSI('G', '%'):
1601 compatibility(OTHER);
1604 case ANSI('@', '%'):
1605 compatibility(OTHER);
1611 termstate = TOPLEVEL; /* default */
1613 if (esc_nargs <= ARGS_MAX) {
1614 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1615 esc_args[esc_nargs - 1] = 0;
1616 esc_args[esc_nargs - 1] =
1617 10 * esc_args[esc_nargs - 1] + c - '0';
1619 termstate = SEEN_CSI;
1620 } else if (c == ';') {
1621 if (++esc_nargs <= ARGS_MAX)
1622 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1623 termstate = SEEN_CSI;
1624 } else if (c < '@') {
1631 termstate = SEEN_CSI;
1633 switch (ANSI(c, esc_query)) {
1634 case 'A': /* move up N lines */
1635 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1636 seen_disp_event = TRUE;
1638 case 'e': /* move down N lines */
1639 compatibility(ANSI);
1642 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1643 seen_disp_event = TRUE;
1645 case ANSI('c', '>'): /* report xterm version */
1646 compatibility(OTHER);
1647 /* this reports xterm version 136 so that VIM can
1648 use the drag messages from the mouse reporting */
1649 ldisc_send("\033[>0;136;0c", 11, 0);
1651 case 'a': /* move right N cols */
1652 compatibility(ANSI);
1655 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1656 seen_disp_event = TRUE;
1658 case 'D': /* move left N cols */
1659 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1660 seen_disp_event = TRUE;
1662 case 'E': /* move down N lines and CR */
1663 compatibility(ANSI);
1664 move(0, curs.y + def(esc_args[0], 1), 1);
1665 seen_disp_event = TRUE;
1667 case 'F': /* move up N lines and CR */
1668 compatibility(ANSI);
1669 move(0, curs.y - def(esc_args[0], 1), 1);
1670 seen_disp_event = TRUE;
1673 case '`': /* set horizontal posn */
1674 compatibility(ANSI);
1675 move(def(esc_args[0], 1) - 1, curs.y, 0);
1676 seen_disp_event = TRUE;
1678 case 'd': /* set vertical posn */
1679 compatibility(ANSI);
1681 (dec_om ? marg_t : 0) + def(esc_args[0],
1684 seen_disp_event = TRUE;
1687 case 'f': /* set horz and vert posns at once */
1689 esc_args[1] = ARG_DEFAULT;
1690 move(def(esc_args[1], 1) - 1,
1691 (dec_om ? marg_t : 0) + def(esc_args[0],
1694 seen_disp_event = TRUE;
1696 case 'J': /* erase screen or parts of it */
1698 unsigned int i = def(esc_args[0], 0) + 1;
1701 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1704 seen_disp_event = TRUE;
1706 case 'K': /* erase line or parts of it */
1708 unsigned int i = def(esc_args[0], 0) + 1;
1711 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1713 seen_disp_event = TRUE;
1715 case 'L': /* insert lines */
1716 compatibility(VT102);
1717 if (curs.y <= marg_b)
1718 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1721 seen_disp_event = TRUE;
1723 case 'M': /* delete lines */
1724 compatibility(VT102);
1725 if (curs.y <= marg_b)
1726 scroll(curs.y, marg_b, def(esc_args[0], 1),
1729 seen_disp_event = TRUE;
1731 case '@': /* insert chars */
1732 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1733 compatibility(VT102);
1734 insch(def(esc_args[0], 1));
1735 seen_disp_event = TRUE;
1737 case 'P': /* delete chars */
1738 compatibility(VT102);
1739 insch(-def(esc_args[0], 1));
1740 seen_disp_event = TRUE;
1742 case 'c': /* terminal type query */
1743 compatibility(VT100);
1744 /* This is the response for a VT102 */
1745 ldisc_send(id_string, strlen(id_string), 0);
1747 case 'n': /* cursor position query */
1748 if (esc_args[0] == 6) {
1750 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1752 ldisc_send(buf, strlen(buf), 0);
1753 } else if (esc_args[0] == 5) {
1754 ldisc_send("\033[0n", 4, 0);
1757 case 'h': /* toggle modes to high */
1759 compatibility(VT100);
1762 for (i = 0; i < esc_nargs; i++)
1763 toggle_mode(esc_args[i], esc_query, TRUE);
1766 case 'l': /* toggle modes to low */
1768 compatibility(VT100);
1771 for (i = 0; i < esc_nargs; i++)
1772 toggle_mode(esc_args[i], esc_query, FALSE);
1775 case 'g': /* clear tabs */
1776 compatibility(VT100);
1777 if (esc_nargs == 1) {
1778 if (esc_args[0] == 0) {
1779 tabs[curs.x] = FALSE;
1780 } else if (esc_args[0] == 3) {
1782 for (i = 0; i < cols; i++)
1787 case 'r': /* set scroll margins */
1788 compatibility(VT100);
1789 if (esc_nargs <= 2) {
1791 top = def(esc_args[0], 1) - 1;
1792 bot = (esc_nargs <= 1
1794 0 ? rows : def(esc_args[1], rows)) - 1;
1797 /* VTTEST Bug 9 - if region is less than 2 lines
1798 * don't change region.
1800 if (bot - top > 0) {
1805 * I used to think the cursor should be
1806 * placed at the top of the newly marginned
1807 * area. Apparently not: VMS TPU falls over
1810 * Well actually it should for Origin mode - RDB
1812 curs.y = (dec_om ? marg_t : 0);
1814 seen_disp_event = TRUE;
1818 case 'm': /* set graphics rendition */
1821 * A VT100 without the AVO only had one attribute, either
1822 * underline or reverse video depending on the cursor type,
1823 * this was selected by CSI 7m.
1826 * This is sometimes DIM, eg on the GIGI and Linux
1828 * This is sometimes INVIS various ANSI.
1830 * This like 22 disables BOLD, DIM and INVIS
1832 * The ANSI colours appear on any terminal that has colour
1833 * (obviously) but the interaction between sgr0 and the
1834 * colours varies but is usually related to the background
1835 * colour erase item.
1836 * The interaction between colour attributes and the mono
1837 * ones is also very implementation dependent.
1839 * The 39 and 49 attributes are likely to be unimplemented.
1842 for (i = 0; i < esc_nargs; i++) {
1843 switch (def(esc_args[i], 0)) {
1844 case 0: /* restore defaults */
1845 curr_attr = ATTR_DEFAULT;
1847 case 1: /* enable bold */
1848 compatibility(VT100AVO);
1849 curr_attr |= ATTR_BOLD;
1851 case 21: /* (enable double underline) */
1852 compatibility(OTHER);
1853 case 4: /* enable underline */
1854 compatibility(VT100AVO);
1855 curr_attr |= ATTR_UNDER;
1857 case 5: /* enable blink */
1858 compatibility(VT100AVO);
1859 curr_attr |= ATTR_BLINK;
1861 case 7: /* enable reverse video */
1862 curr_attr |= ATTR_REVERSE;
1864 case 10: /* SCO acs off */
1865 compatibility(SCOANSI);
1867 case 11: /* SCO acs on */
1868 compatibility(SCOANSI);
1870 case 12: /* SCO acs on flipped */
1871 compatibility(SCOANSI);
1873 case 22: /* disable bold */
1874 compatibility2(OTHER, VT220);
1875 curr_attr &= ~ATTR_BOLD;
1877 case 24: /* disable underline */
1878 compatibility2(OTHER, VT220);
1879 curr_attr &= ~ATTR_UNDER;
1881 case 25: /* disable blink */
1882 compatibility2(OTHER, VT220);
1883 curr_attr &= ~ATTR_BLINK;
1885 case 27: /* disable reverse video */
1886 compatibility2(OTHER, VT220);
1887 curr_attr &= ~ATTR_REVERSE;
1898 curr_attr &= ~ATTR_FGMASK;
1900 (esc_args[i] - 30) << ATTR_FGSHIFT;
1902 case 39: /* default-foreground */
1903 curr_attr &= ~ATTR_FGMASK;
1904 curr_attr |= ATTR_DEFFG;
1915 curr_attr &= ~ATTR_BGMASK;
1917 (esc_args[i] - 40) << ATTR_BGSHIFT;
1919 case 49: /* default-background */
1920 curr_attr &= ~ATTR_BGMASK;
1921 curr_attr |= ATTR_DEFBG;
1926 erase_char = (' ' | ATTR_ASCII |
1928 (ATTR_FGMASK | ATTR_BGMASK)));
1931 case 's': /* save cursor */
1934 case 'u': /* restore cursor */
1936 seen_disp_event = TRUE;
1938 case 't': /* set page size - ie window height */
1940 * VT340/VT420 sequence DECSLPP, DEC only allows values
1941 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1942 * illegal values (eg first arg 1..9) for window changing
1945 compatibility(VT340TEXT);
1947 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1948 request_resize(cols, def(esc_args[0], 24));
1953 compatibility(SCOANSI);
1954 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1957 seen_disp_event = TRUE;
1960 compatibility(SCOANSI);
1961 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1964 seen_disp_event = TRUE;
1966 case ANSI('|', '*'):
1967 /* VT420 sequence DECSNLS
1968 * Set number of lines on screen
1969 * VT420 uses VGA like hardware and can support any size in
1970 * reasonable range (24..49 AIUI) with no default specified.
1972 compatibility(VT420);
1973 if (esc_nargs == 1 && esc_args[0] > 0) {
1974 request_resize(cols, def(esc_args[0], cfg.height));
1978 case ANSI('|', '$'):
1979 /* VT340/VT420 sequence DECSCPP
1980 * Set number of columns per page
1981 * Docs imply range is only 80 or 132, but I'll allow any.
1983 compatibility(VT340TEXT);
1984 if (esc_nargs <= 1) {
1985 request_resize(def(esc_args[0], cfg.width), rows);
1989 case 'X': /* write N spaces w/o moving cursor */
1990 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1991 compatibility(ANSIMIN);
1993 int n = def(esc_args[0], 1);
1995 unsigned long *p = cpos;
1996 if (n > cols - curs.x)
2000 check_selection(curs, cursplus);
2003 seen_disp_event = TRUE;
2006 case 'x': /* report terminal characteristics */
2007 compatibility(VT100);
2010 int i = def(esc_args[0], 0);
2011 if (i == 0 || i == 1) {
2012 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2014 ldisc_send(buf, 20, 0);
2018 case 'Z': /* BackTab for xterm */
2019 compatibility(OTHER);
2021 int i = def(esc_args[0], 1);
2022 pos old_curs = curs;
2024 for(;i>0 && curs.x>0; i--) {
2027 } while (curs.x >0 && !tabs[curs.x]);
2030 check_selection(old_curs, curs);
2033 case ANSI('L', '='):
2034 compatibility(OTHER);
2035 use_bce = (esc_args[0] <= 0);
2036 erase_char = ERASE_CHAR;
2038 erase_char = (' ' | ATTR_ASCII |
2040 (ATTR_FGMASK | ATTR_BGMASK)));
2042 case ANSI('E', '='):
2043 compatibility(OTHER);
2044 blink_is_real = (esc_args[0] >= 1);
2046 case ANSI('p', '"'):
2047 /* Allow the host to make this emulator a 'perfect' VT102.
2048 * This first appeared in the VT220, but we do need to get
2049 * back to PuTTY mode so I won't check it.
2051 * The arg in 40..42,50 are a PuTTY extension.
2052 * The 2nd arg, 8bit vs 7bit is not checked.
2054 * Setting VT102 mode should also change the Fkeys to
2055 * generate PF* codes as a real VT102 has no Fkeys.
2056 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2059 * Note ESC c will NOT change this!
2062 switch (esc_args[0]) {
2064 compatibility_level &= ~TM_VTXXX;
2065 compatibility_level |= TM_VT102;
2068 compatibility_level &= ~TM_VTXXX;
2069 compatibility_level |= TM_VT220;
2073 if (esc_args[0] > 60 && esc_args[0] < 70)
2074 compatibility_level |= TM_VTXXX;
2078 compatibility_level &= TM_VTXXX;
2081 compatibility_level = TM_PUTTY;
2084 compatibility_level = TM_SCOANSI;
2088 compatibility_level = TM_PUTTY;
2094 /* Change the response to CSI c */
2095 if (esc_args[0] == 50) {
2098 strcpy(id_string, "\033[?");
2099 for (i = 1; i < esc_nargs; i++) {
2101 strcat(id_string, ";");
2102 sprintf(lbuf, "%d", esc_args[i]);
2103 strcat(id_string, lbuf);
2105 strcat(id_string, "c");
2108 /* Is this a good idea ?
2109 * Well we should do a soft reset at this point ...
2111 if (!has_compat(VT420) && has_compat(VT100)) {
2113 request_resize(132, 24);
2115 request_resize(80, 24);
2124 case 'P': /* Linux palette sequence */
2125 termstate = SEEN_OSC_P;
2128 case 'R': /* Linux palette reset */
2131 termstate = TOPLEVEL;
2133 case 'W': /* word-set */
2134 termstate = SEEN_OSC_W;
2147 esc_args[0] = 10 * esc_args[0] + c - '0';
2151 * Grotty hack to support xterm and DECterm title
2152 * sequences concurrently.
2154 if (esc_args[0] == 2) {
2158 /* else fall through */
2160 termstate = OSC_STRING;
2166 * This OSC stuff is EVIL. It takes just one character to get into
2167 * sysline mode and it's not initially obvious how to get out.
2168 * So I've added CR and LF as string aborts.
2169 * This shouldn't effect compatibility as I believe embedded
2170 * control characters are supposed to be interpreted (maybe?)
2171 * and they don't display anything useful anyway.
2175 if (c == '\n' || c == '\r') {
2176 termstate = TOPLEVEL;
2177 } else if (c == 0234 || c == '\007') {
2179 * These characters terminate the string; ST and BEL
2180 * terminate the sequence and trigger instant
2181 * processing of it, whereas ESC goes back to SEEN_ESC
2182 * mode unless it is followed by \, in which case it is
2183 * synonymous with ST in the first place.
2186 termstate = TOPLEVEL;
2187 } else if (c == '\033')
2188 termstate = OSC_MAYBE_ST;
2189 else if (osc_strlen < OSC_STR_MAX)
2190 osc_string[osc_strlen++] = c;
2194 int max = (osc_strlen == 0 ? 21 : 16);
2196 if (c >= '0' && c <= '9')
2198 else if (c >= 'A' && c <= 'A' + max - 10)
2200 else if (c >= 'a' && c <= 'a' + max - 10)
2203 termstate = TOPLEVEL;
2206 osc_string[osc_strlen++] = val;
2207 if (osc_strlen >= 7) {
2208 palette_set(osc_string[0],
2209 osc_string[1] * 16 + osc_string[2],
2210 osc_string[3] * 16 + osc_string[4],
2211 osc_string[5] * 16 + osc_string[6]);
2213 termstate = TOPLEVEL;
2229 esc_args[0] = 10 * esc_args[0] + c - '0';
2232 termstate = OSC_STRING;
2237 termstate = TOPLEVEL;
2238 seen_disp_event = TRUE;
2241 move(curs.x, curs.y - 1, 1);
2244 move(curs.x, curs.y + 1, 1);
2247 move(curs.x + 1, curs.y, 1);
2250 move(curs.x - 1, curs.y, 1);
2253 * From the VT100 Manual
2254 * NOTE: The special graphics characters in the VT100
2255 * are different from those in the VT52
2257 * From VT102 manual:
2258 * 137 _ Blank - Same
2259 * 140 ` Reserved - Humm.
2260 * 141 a Solid rectangle - Similar
2261 * 142 b 1/ - Top half of fraction for the
2262 * 143 c 3/ - subscript numbers below.
2265 * 146 f Degrees - Same
2266 * 147 g Plus or minus - Same
2268 * 151 i Ellipsis (dots)
2271 * 154 l Bar at scan 0
2272 * 155 m Bar at scan 1
2273 * 156 n Bar at scan 2
2274 * 157 o Bar at scan 3 - Similar
2275 * 160 p Bar at scan 4 - Similar
2276 * 161 q Bar at scan 5 - Similar
2277 * 162 r Bar at scan 6 - Same
2278 * 163 s Bar at scan 7 - Similar
2293 cset_attr[cset = 0] = ATTR_LINEDRW;
2296 cset_attr[cset = 0] = ATTR_ASCII;
2303 scroll(0, rows - 1, -1, TRUE);
2304 else if (curs.y > 0)
2310 erase_lots(FALSE, FALSE, TRUE);
2314 erase_lots(TRUE, FALSE, TRUE);
2318 /* XXX Print cursor line */
2321 /* XXX Start controller mode */
2324 /* XXX Stop controller mode */
2328 termstate = VT52_Y1;
2331 ldisc_send("\033/Z", 3, 0);
2334 app_keypad_keys = TRUE;
2337 app_keypad_keys = FALSE;
2340 /* XXX This should switch to VT100 mode not current or default
2341 * VT mode. But this will only have effect in a VT220+
2345 blink_is_real = cfg.blinktext;
2349 /* XXX Enter auto print mode */
2352 /* XXX Exit auto print mode */
2355 /* XXX Print screen */
2361 /* compatibility(ATARI) */
2363 erase_lots(FALSE, FALSE, TRUE);
2367 /* compatibility(ATARI) */
2368 if (curs.y <= marg_b)
2369 scroll(curs.y, marg_b, -1, FALSE);
2372 /* compatibility(ATARI) */
2373 if (curs.y <= marg_b)
2374 scroll(curs.y, marg_b, 1, TRUE);
2377 /* compatibility(ATARI) */
2378 termstate = VT52_FG;
2381 /* compatibility(ATARI) */
2382 termstate = VT52_BG;
2385 /* compatibility(ATARI) */
2386 erase_lots(FALSE, TRUE, FALSE);
2390 /* compatibility(ATARI) */
2394 /* compatibility(ATARI) */
2397 /* case 'j': Save cursor position - broken on ST */
2398 /* case 'k': Restore cursor position */
2400 /* compatibility(ATARI) */
2401 erase_lots(TRUE, TRUE, TRUE);
2407 /* compatibility(ATARI) */
2408 erase_lots(TRUE, TRUE, FALSE);
2411 /* compatibility(ATARI) */
2412 curr_attr |= ATTR_REVERSE;
2415 /* compatibility(ATARI) */
2416 curr_attr &= ~ATTR_REVERSE;
2418 case 'v': /* wrap Autowrap on - Wyse style */
2419 /* compatibility(ATARI) */
2422 case 'w': /* Autowrap off */
2423 /* compatibility(ATARI) */
2428 /* compatibility(OTHER) */
2430 curr_attr = ATTR_DEFAULT;
2432 erase_char = (' ' | ATTR_ASCII |
2434 (ATTR_FGMASK | ATTR_BGMASK)));
2437 /* compatibility(VI50) */
2438 curr_attr |= ATTR_UNDER;
2441 /* compatibility(VI50) */
2442 curr_attr &= ~ATTR_UNDER;
2445 /* compatibility(VI50) */
2447 curr_attr |= ATTR_BOLD;
2450 /* compatibility(VI50) */
2452 curr_attr &= ~ATTR_BOLD;
2458 termstate = VT52_Y2;
2459 move(curs.x, c - ' ', 0);
2462 termstate = TOPLEVEL;
2463 move(c - ' ', curs.y, 0);
2468 termstate = TOPLEVEL;
2469 curr_attr &= ~ATTR_FGMASK;
2470 curr_attr &= ~ATTR_BOLD;
2471 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2472 if ((c & 0x8) || vt52_bold)
2473 curr_attr |= ATTR_BOLD;
2476 erase_char = (' ' | ATTR_ASCII |
2477 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2480 termstate = TOPLEVEL;
2481 curr_attr &= ~ATTR_BGMASK;
2482 curr_attr &= ~ATTR_BLINK;
2483 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2485 /* Note: bold background */
2487 curr_attr |= ATTR_BLINK;
2490 erase_char = (' ' | ATTR_ASCII |
2491 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2494 default: break; /* placate gcc warning about enum use */
2496 if (selstate != NO_SELECTION) {
2497 pos cursplus = curs;
2499 check_selection(curs, cursplus);
2506 * Compare two lines to determine whether they are sufficiently
2507 * alike to scroll-optimise one to the other. Return the degree of
2510 static int linecmp(unsigned long *a, unsigned long *b)
2514 for (i = n = 0; i < cols; i++)
2515 n += (*a++ == *b++);
2521 * Given a context, update the window. Out of paranoia, we don't
2522 * allow WM_PAINT responses to do scrolling optimisations.
2524 static void do_paint(Context ctx, int may_optimise)
2526 int i, j, our_curs_y;
2527 unsigned long rv, cursor;
2530 long cursor_background = ERASE_CHAR;
2534 * Check the visual bell state.
2537 ticks = GetTickCount();
2538 if (ticks - vbell_timeout >= 0)
2542 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2545 * screen array, disptop, scrtop,
2547 * cfg.blinkpc, blink_is_real, tblinker,
2548 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2551 /* Has the cursor position or type changed ? */
2554 if (blinker || !cfg.blink_cur)
2555 cursor = TATTR_ACTCURS;
2559 cursor = TATTR_PASCURS;
2561 cursor |= TATTR_RIGHTCURS;
2564 our_curs_y = curs.y - disptop;
2566 if (dispcurs && (curstype != cursor ||
2568 disptext + our_curs_y * (cols + 1) + curs.x)) {
2569 if (dispcurs > disptext &&
2570 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2571 dispcurs[-1] |= ATTR_INVALID;
2572 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2573 dispcurs[1] |= ATTR_INVALID;
2574 *dispcurs |= ATTR_INVALID;
2579 /* The normal screen data */
2580 for (i = 0; i < rows; i++) {
2581 unsigned long *ldata;
2583 int idx, dirty_line, dirty_run;
2584 unsigned long attr = 0;
2585 int updated_line = 0;
2588 int last_run_dirty = 0;
2590 scrpos.y = i + disptop;
2591 ldata = lineptr(scrpos.y);
2592 lattr = (ldata[cols] & LATTR_MODE);
2594 idx = i * (cols + 1);
2595 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2596 disptext[idx + cols] = ldata[cols];
2598 for (j = 0; j < cols; j++, idx++) {
2599 unsigned long tattr, tchar;
2600 unsigned long *d = ldata + j;
2604 tchar = (*d & (CHAR_MASK | CSET_MASK));
2605 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2606 switch (tchar & CSET_MASK) {
2608 tchar = unitab_line[tchar & 0xFF];
2611 tchar = unitab_xterm[tchar & 0xFF];
2614 tchar = unitab_scoacs[tchar&0xFF];
2617 tattr |= (tchar & CSET_MASK);
2619 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2622 /* Video reversing things */
2624 ^ (posle(selstart, scrpos) &&
2625 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2627 /* 'Real' blinking ? */
2628 if (blink_is_real && (tattr & ATTR_BLINK)) {
2629 if (has_focus && tblinker) {
2631 tattr &= ~CSET_MASK;
2634 tattr &= ~ATTR_BLINK;
2638 * Check the font we'll _probably_ be using to see if
2639 * the character is wide when we don't want it to be.
2641 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2642 if ((tattr & ATTR_WIDE) == 0 &&
2643 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2644 tattr |= ATTR_NARROW;
2645 } else if (disptext[idx]&ATTR_NARROW)
2646 tattr |= ATTR_NARROW;
2648 /* Cursor here ? Save the 'background' */
2649 if (i == our_curs_y && j == curs.x) {
2650 cursor_background = tattr | tchar;
2651 dispcurs = disptext + idx;
2654 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2657 break_run = (tattr != attr || j - start >= sizeof(ch));
2659 /* Special hack for VT100 Linedraw glyphs */
2660 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2661 && tchar <= 0xBD) break_run = TRUE;
2663 if (!dbcs_screenfont && !dirty_line) {
2664 if ((tchar | tattr) == disptext[idx])
2666 else if (!dirty_run && ccount == 1)
2671 if ((dirty_run || last_run_dirty) && ccount > 0) {
2672 do_text(ctx, start, i, ch, ccount, attr, lattr);
2678 if (dbcs_screenfont)
2679 last_run_dirty = dirty_run;
2680 dirty_run = dirty_line;
2683 if ((tchar | tattr) != disptext[idx])
2685 ch[ccount++] = (char) tchar;
2686 disptext[idx] = tchar | tattr;
2688 /* If it's a wide char step along to the next one. */
2689 if (tattr & ATTR_WIDE) {
2693 /* Cursor is here ? Ouch! */
2694 if (i == our_curs_y && j == curs.x) {
2695 cursor_background = *d;
2696 dispcurs = disptext + idx;
2698 if (disptext[idx] != *d)
2704 if (dirty_run && ccount > 0) {
2705 do_text(ctx, start, i, ch, ccount, attr, lattr);
2709 /* Cursor on this line ? (and changed) */
2710 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2711 ch[0] = (char) (cursor_background & CHAR_MASK);
2712 attr = (cursor_background & ATTR_MASK) | cursor;
2713 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2720 * Flick the switch that says if blinking things should be shown or hidden.
2723 void term_blink(int flg)
2725 static long last_blink = 0;
2726 static long last_tblink = 0;
2727 long now, blink_diff;
2729 now = GetTickCount();
2730 blink_diff = now - last_tblink;
2732 /* Make sure the text blinks no more than 2Hz */
2733 if (blink_diff < 0 || blink_diff > 450) {
2735 tblinker = !tblinker;
2744 blink_diff = now - last_blink;
2746 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2747 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2755 * Invalidate the whole screen so it will be repainted in full.
2757 void term_invalidate(void)
2761 for (i = 0; i < rows * (cols + 1); i++)
2762 disptext[i] = ATTR_INVALID;
2766 * Paint the window in response to a WM_PAINT message.
2768 void term_paint(Context ctx, int left, int top, int right, int bottom)
2771 if (left < 0) left = 0;
2772 if (top < 0) top = 0;
2773 if (right >= cols) right = cols-1;
2774 if (bottom >= rows) bottom = rows-1;
2776 for (i = top; i <= bottom && i < rows; i++) {
2777 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2778 for (j = left; j <= right && j < cols; j++)
2779 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2781 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2782 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2785 /* This should happen soon enough, also for some reason it sometimes
2786 * fails to actually do anything when re-sizing ... painting the wrong
2790 do_paint (ctx, FALSE);
2794 * Attempt to scroll the scrollback. The second parameter gives the
2795 * position we want to scroll to; the first is +1 to denote that
2796 * this position is relative to the beginning of the scrollback, -1
2797 * to denote it is relative to the end, and 0 to denote that it is
2798 * relative to the current position.
2800 void term_scroll(int rel, int where)
2802 int sbtop = -count234(scrollback);
2804 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2805 if (disptop < sbtop)
2813 static void clipme(pos top, pos bottom)
2816 wchar_t *wbptr; /* where next char goes within workbuf */
2817 int wblen = 0; /* workbuf len */
2818 int buflen; /* amount of memory allocated to workbuf */
2820 buflen = 5120; /* Default size */
2821 workbuf = smalloc(buflen * sizeof(wchar_t));
2822 wbptr = workbuf; /* start filling here */
2824 while (poslt(top, bottom)) {
2826 unsigned long *ldata = lineptr(top.y);
2832 if (!(ldata[cols] & LATTR_WRAPPED)) {
2833 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2834 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2835 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2836 && poslt(top, nlpos))
2838 if (poslt(nlpos, bottom))
2841 while (poslt(top, bottom) && poslt(top, nlpos)) {
2844 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2846 wchar_t cbuf[16], *p;
2847 int uc = (ldata[top.x] & 0xFFFF);
2850 if (uc == UCSWIDE) {
2855 switch (uc & CSET_MASK) {
2858 uc = unitab_xterm[uc & 0xFF];
2862 uc = unitab_line[uc & 0xFF];
2865 uc = unitab_scoacs[uc&0xFF];
2868 switch (uc & CSET_MASK) {
2870 uc = unitab_font[uc & 0xFF];
2873 uc = unitab_oemcp[uc & 0xFF];
2877 set = (uc & CSET_MASK);
2878 c = (uc & CHAR_MASK);
2882 if (DIRECT_FONT(uc)) {
2883 if (c >= ' ' && c != 0x7F) {
2884 unsigned char buf[4];
2887 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2889 buf[1] = (unsigned char) ldata[top.x + 1];
2890 rv = MultiByteToWideChar(font_codepage,
2891 0, buf, 2, wbuf, 4);
2895 rv = MultiByteToWideChar(font_codepage,
2896 0, buf, 1, wbuf, 4);
2900 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2907 for (p = cbuf; *p; p++) {
2908 /* Enough overhead for trailing NL and nul */
2909 if (wblen >= buflen - 16) {
2912 sizeof(wchar_t) * (buflen += 100));
2913 wbptr = workbuf + wblen;
2922 for (i = 0; i < sel_nl_sz; i++) {
2924 *wbptr++ = sel_nl[i];
2932 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2933 if (buflen > 0) /* indicates we allocated this buffer */
2937 void term_copyall(void)
2940 top.y = -count234(scrollback);
2946 * The wordness array is mainly for deciding the disposition of the US-ASCII
2949 static int wordtype(int uc)
2952 int start, end, ctype;
2953 } *wptr, ucs_words[] = {
2959 0x037e, 0x037e, 1}, /* Greek question mark */
2961 0x0387, 0x0387, 1}, /* Greek ano teleia */
2963 0x055a, 0x055f, 1}, /* Armenian punctuation */
2965 0x0589, 0x0589, 1}, /* Armenian full stop */
2967 0x0700, 0x070d, 1}, /* Syriac punctuation */
2969 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2971 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2973 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2975 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2977 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2979 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2981 0x2000, 0x200a, 0}, /* Various spaces */
2983 0x2070, 0x207f, 2}, /* superscript */
2985 0x2080, 0x208f, 2}, /* subscript */
2987 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2989 0x3000, 0x3000, 0}, /* ideographic space */
2991 0x3001, 0x3020, 1}, /* ideographic punctuation */
2993 0x303f, 0x309f, 3}, /* Hiragana */
2995 0x30a0, 0x30ff, 3}, /* Katakana */
2997 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2999 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3001 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3003 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3005 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3007 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3009 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3011 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3013 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3018 uc &= (CSET_MASK | CHAR_MASK);
3020 switch (uc & CSET_MASK) {
3022 uc = unitab_xterm[uc & 0xFF];
3025 uc = unitab_line[uc & 0xFF];
3028 uc = unitab_scoacs[uc&0xFF];
3031 switch (uc & CSET_MASK) {
3033 uc = unitab_font[uc & 0xFF];
3036 uc = unitab_oemcp[uc & 0xFF];
3040 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3041 * fail as there's such a thing as a double width space. :-(
3043 if (dbcs_screenfont && font_codepage == line_codepage)
3047 return wordness[uc];
3049 for (wptr = ucs_words; wptr->start; wptr++) {
3050 if (uc >= wptr->start && uc <= wptr->end)
3058 * Spread the selection outwards according to the selection mode.
3060 static pos sel_spread_half(pos p, int dir)
3062 unsigned long *ldata;
3065 ldata = lineptr(p.y);
3070 * In this mode, every character is a separate unit, except
3071 * for runs of spaces at the end of a non-wrapping line.
3073 if (!(ldata[cols] & LATTR_WRAPPED)) {
3074 unsigned long *q = ldata + cols;
3075 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3077 if (q == ldata + cols)
3079 if (p.x >= q - ldata)
3080 p.x = (dir == -1 ? q - ldata : cols - 1);
3085 * In this mode, the units are maximal runs of characters
3086 * whose `wordness' has the same value.
3088 wvalue = wordtype(ldata[p.x]);
3090 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3093 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3099 * In this mode, every line is a unit.
3101 p.x = (dir == -1 ? 0 : cols - 1);
3107 static void sel_spread(void)
3109 selstart = sel_spread_half(selstart, -1);
3111 selend = sel_spread_half(selend, +1);
3115 void term_do_paste(void)
3120 get_clip(&data, &len);
3125 sfree(paste_buffer);
3126 paste_pos = paste_hold = paste_len = 0;
3127 paste_buffer = smalloc(len * sizeof(wchar_t));
3130 while (p < data + len) {
3131 while (p < data + len &&
3132 !(p <= data + len - sel_nl_sz &&
3133 !memcmp(p, sel_nl, sizeof(sel_nl))))
3138 for (i = 0; i < p - q; i++) {
3139 paste_buffer[paste_len++] = q[i];
3143 if (p <= data + len - sel_nl_sz &&
3144 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3145 paste_buffer[paste_len++] = '\r';
3151 /* Assume a small paste will be OK in one go. */
3152 if (paste_len < 256) {
3153 luni_send(paste_buffer, paste_len, 0);
3155 sfree(paste_buffer);
3157 paste_pos = paste_hold = paste_len = 0;
3160 get_clip(NULL, NULL);
3163 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3164 int shift, int ctrl)
3167 unsigned long *ldata;
3168 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3172 if (a == MA_DRAG && !raw_mouse)
3177 if (a == MA_DRAG && !raw_mouse)
3190 selpoint.y = y + disptop;
3192 ldata = lineptr(selpoint.y);
3193 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3197 int encstate = 0, r, c;
3199 static int is_down = 0;
3203 encstate = 0x20; /* left button down */
3214 case MBT_WHEEL_DOWN:
3217 default: break; /* placate gcc warning about enum use */
3221 if (xterm_mouse == 1)
3234 default: break; /* placate gcc warning about enum use */
3243 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3244 ldisc_send(abuf, 6, 0);
3248 b = translate_button(b);
3250 if (b == MBT_SELECT && a == MA_CLICK) {
3252 selstate = ABOUT_TO;
3253 selanchor = selpoint;
3255 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3257 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3258 selstate = DRAGGING;
3259 selstart = selanchor = selpoint;
3263 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3264 (b == MBT_EXTEND && a != MA_RELEASE)) {
3265 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3267 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3268 if (posdiff(selpoint, selstart) <
3269 posdiff(selend, selstart) / 2) {
3273 selanchor = selstart;
3275 selstate = DRAGGING;
3277 if (selstate != ABOUT_TO && selstate != DRAGGING)
3278 selanchor = selpoint;
3279 selstate = DRAGGING;
3280 if (poslt(selpoint, selanchor)) {
3281 selstart = selpoint;
3285 selstart = selanchor;
3290 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3291 if (selstate == DRAGGING) {
3293 * We've completed a selection. We now transfer the
3294 * data to the clipboard.
3296 clipme(selstart, selend);
3297 selstate = SELECTED;
3299 selstate = NO_SELECTION;
3300 } else if (b == MBT_PASTE
3301 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3312 sfree(paste_buffer);
3319 static long last_paste = 0;
3320 long now, paste_diff;
3325 /* Don't wait forever to paste */
3327 now = GetTickCount();
3328 paste_diff = now - last_paste;
3329 if (paste_diff >= 0 && paste_diff < 450)
3334 while (paste_pos < paste_len) {
3336 while (n + paste_pos < paste_len) {
3337 if (paste_buffer[paste_pos + n++] == '\r')
3340 luni_send(paste_buffer + paste_pos, n, 0);
3343 if (paste_pos < paste_len) {
3348 sfree(paste_buffer);
3353 static void deselect(void)
3355 selstate = NO_SELECTION;
3356 selstart.x = selstart.y = selend.x = selend.y = 0;
3359 void term_deselect(void)
3365 int term_ldisc(int option)
3367 if (option == LD_ECHO)
3368 return term_echoing;
3369 if (option == LD_EDIT)
3370 return term_editing;
3375 * from_backend(), to get data from the backend for the terminal.
3377 int from_backend(int is_stderr, char *data, int len)
3379 bufchain_add(&inbuf, data, len);
3382 * term_out() always completely empties inbuf. Therefore,
3383 * there's no reason at all to return anything other than zero
3384 * from this function, because there _can't_ be a question of
3385 * the remote side needing to wait until term_out() has cleared
3388 * This is a slightly suboptimal way to deal with SSH2 - in
3389 * principle, the window mechanism would allow us to continue
3390 * to accept data on forwarded ports and X connections even
3391 * while the terminal processing was going slowly - but we
3392 * can't do the 100% right thing without moving the terminal
3393 * processing into a separate thread, and that might hurt
3394 * portability. So we manage stdout buffering the old SSH1 way:
3395 * if the terminal processing goes slowly, the whole SSH
3396 * connection stops accepting data until it's ready.
3398 * In practice, I can't imagine this causing serious trouble.
3404 * Log session traffic.
3406 void logtraffic(unsigned char c, int logmode)
3408 if (cfg.logtype > 0) {
3409 if (cfg.logtype == logmode) {
3410 /* deferred open file from pgm start? */
3419 void settimstr(char *ta, int no_sec);
3420 char *subslfcode(char *dest, char *src, char *dstrt);
3421 char *stpncpy(char *dst, const char *src, size_t maxlen);
3423 char currlogfilename[FILENAME_MAX];
3425 /* open log file append/overwrite mode */
3435 sprintf(writemod, "wb"); /* default to rewrite */
3438 tm = *localtime(&t);
3440 /* substitute special codes in file name */
3441 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3443 lgfp = fopen(currlogfilename, "r"); /* file already present? */
3447 i = askappend(currlogfilename);
3449 writemod[0] = 'a'; /* set append mode */
3450 else if (i == 0) { /* cancelled */
3452 cfg.logtype = 0; /* disable logging */
3457 lgfp = fopen(currlogfilename, writemod);
3458 if (lgfp) { /* enter into event log */
3459 sprintf(buf, "%s session log (%s mode) to file : ",
3460 (writemod[0] == 'a') ? "Appending" : "Writing new",
3461 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3462 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3463 /* Make sure we do not exceed the output buffer size */
3464 strncat(buf, currlogfilename, 128);
3465 buf[strlen(buf)] = '\0';
3468 /* --- write header line into log file */
3469 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3470 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
3472 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3476 void logfclose(void)
3485 * translate format codes into time/date strings
3486 * and insert them into log file name
3488 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3490 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3491 char buf[10], *bufp;
3493 char *ds = d; /* save start pos. */
3494 int len = FILENAME_MAX-1;
3497 /* Let (bufp, len) be the string to append. */
3498 bufp = buf; /* don't usually override this */
3502 if (*s) switch (c = *s++, tolower(c)) {
3504 size = strftime(buf, sizeof(buf), "%Y", tm);
3507 size = strftime(buf, sizeof(buf), "%m", tm);
3510 size = strftime(buf, sizeof(buf), "%d", tm);
3513 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3517 size = strlen(bufp);
3531 memcpy(d, bufp, size);