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 unsigned long rvbell_startpoint;/* 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 /* turn off a previous vbell to avoid inconsistencies */
881 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
883 if (rvideo && !state && /* we're turning it off... */
884 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
885 /* If there's no vbell timeout already, or this one lasts
886 * longer, replace vbell_timeout with ours. */
888 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
889 vbell_startpoint = rvbell_startpoint;
890 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
891 } else if (!rvideo && state) {
892 /* This is an ON, so we notice the time and save it. */
893 rvbell_startpoint = ticks;
896 seen_disp_event = TRUE;
900 case 6: /* DEC origin mode */
903 case 7: /* auto wrap */
906 case 8: /* auto key repeat */
909 case 10: /* set local edit mode */
910 term_editing = state;
911 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
913 case 25: /* enable/disable cursor */
914 compatibility2(OTHER, VT220);
916 seen_disp_event = TRUE;
918 case 47: /* alternate screen */
919 compatibility(OTHER);
924 case 1000: /* xterm mouse 1 */
925 xterm_mouse = state ? 1 : 0;
926 set_raw_mouse_mode(state);
928 case 1002: /* xterm mouse 2 */
929 xterm_mouse = state ? 2 : 0;
930 set_raw_mouse_mode(state);
934 case 4: /* set insert mode */
935 compatibility(VT102);
938 case 12: /* set echo mode */
939 term_echoing = !state;
940 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
942 case 20: /* Return sends ... */
943 cr_lf_return = state;
945 case 34: /* Make cursor BIG */
946 compatibility2(OTHER, VT220);
952 * Process an OSC sequence: set window title or icon name.
954 static void do_osc(void)
958 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
960 osc_string[osc_strlen] = '\0';
961 switch (esc_args[0]) {
964 set_icon(osc_string);
965 if (esc_args[0] == 1)
967 /* fall through: parameter 0 means set both */
970 set_title(osc_string);
977 * Remove everything currently in `inbuf' and stick it up on the
978 * in-memory display. There's a big state machine in here to
979 * process escape sequences...
984 unsigned char localbuf[256], *chars;
989 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
993 bufchain_prefix(&inbuf, &ret, &nchars);
994 if (nchars > sizeof(localbuf))
995 nchars = sizeof(localbuf);
996 memcpy(localbuf, ret, nchars);
997 bufchain_consume(&inbuf, nchars);
999 assert(chars != NULL);
1005 * Optionally log the session traffic to a file. Useful for
1006 * debugging and possibly also useful for actual logging.
1008 if (cfg.logtype == LGTYP_DEBUG)
1009 logtraffic((unsigned char) c, LGTYP_DEBUG);
1015 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1016 * be able to display 8-bit characters, but I'll let that go 'cause
1020 /* First see about all those translations. */
1021 if (termstate == TOPLEVEL) {
1023 switch (utf_state) {
1026 /* UTF-8 must be stateless so we ignore iso2022. */
1027 if (unitab_ctrl[c] != 0xFF)
1029 else c = ((unsigned char)c) | ATTR_ASCII;
1031 } else if ((c & 0xe0) == 0xc0) {
1032 utf_size = utf_state = 1;
1033 utf_char = (c & 0x1f);
1034 } else if ((c & 0xf0) == 0xe0) {
1035 utf_size = utf_state = 2;
1036 utf_char = (c & 0x0f);
1037 } else if ((c & 0xf8) == 0xf0) {
1038 utf_size = utf_state = 3;
1039 utf_char = (c & 0x07);
1040 } else if ((c & 0xfc) == 0xf8) {
1041 utf_size = utf_state = 4;
1042 utf_char = (c & 0x03);
1043 } else if ((c & 0xfe) == 0xfc) {
1044 utf_size = utf_state = 5;
1045 utf_char = (c & 0x01);
1056 if ((c & 0xC0) != 0x80) {
1062 utf_char = (utf_char << 6) | (c & 0x3f);
1068 /* Is somebody trying to be evil! */
1070 (c < 0x800 && utf_size >= 2) ||
1071 (c < 0x10000 && utf_size >= 3) ||
1072 (c < 0x200000 && utf_size >= 4) ||
1073 (c < 0x4000000 && utf_size >= 5))
1076 /* Unicode line separator and paragraph separator are CR-LF */
1077 if (c == 0x2028 || c == 0x2029)
1080 /* High controls are probably a Baaad idea too. */
1084 /* The UTF-16 surrogates are not nice either. */
1085 /* The standard give the option of decoding these:
1086 * I don't want to! */
1087 if (c >= 0xD800 && c < 0xE000)
1090 /* ISO 10646 characters now limited to UTF-16 range. */
1094 /* This is currently a TagPhobic application.. */
1095 if (c >= 0xE0000 && c <= 0xE007F)
1098 /* U+FEFF is best seen as a null. */
1101 /* But U+FFFE is an error. */
1102 if (c == 0xFFFE || c == 0xFFFF)
1105 /* Oops this is a 16bit implementation */
1110 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1112 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1114 if (sco_acs == 2) c ^= 0x80;
1117 switch (cset_attr[cset]) {
1119 * Linedraw characters are different from 'ESC ( B'
1120 * only for a small range. For ones outside that
1121 * range, make sure we use the same font as well as
1122 * the same encoding.
1125 if (unitab_ctrl[c] != 0xFF)
1128 c = ((unsigned char) c) | ATTR_LINEDRW;
1132 /* If UK-ASCII, make the '#' a LineDraw Pound */
1134 c = '}' | ATTR_LINEDRW;
1137 /*FALLTHROUGH*/ case ATTR_ASCII:
1138 if (unitab_ctrl[c] != 0xFF)
1141 c = ((unsigned char) c) | ATTR_ASCII;
1144 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1150 /* How about C1 controls ? */
1151 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1152 has_compat(VT220)) {
1153 termstate = SEEN_ESC;
1155 c = '@' + (c & 0x1F);
1158 /* Or the GL control. */
1159 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1160 if (curs.x && !wrapnext)
1164 *cpos = (' ' | curr_attr | ATTR_ASCII);
1166 /* Or normal C0 controls. */
1167 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1169 case '\005': /* terminal type query */
1170 /* Strictly speaking this is VT100 but a VT100 defaults to
1171 * no response. Other terminals respond at their option.
1173 * Don't put a CR in the default string as this tends to
1174 * upset some weird software.
1176 * An xterm returns "xterm" (5 characters)
1178 compatibility(ANSIMIN);
1180 char abuf[256], *s, *d;
1182 for (s = cfg.answerback, d = abuf; *s; s++) {
1184 if (*s >= 'a' && *s <= 'z')
1185 *d++ = (*s - ('a' - 1));
1186 else if ((*s >= '@' && *s <= '_') ||
1187 *s == '?' || (*s & 0x80))
1192 } else if (*s == '^') {
1197 lpage_send(CP_ACP, abuf, d - abuf, 0);
1202 struct beeptime *newbeep;
1203 unsigned long ticks;
1205 ticks = GetTickCount();
1207 if (!beep_overloaded) {
1208 newbeep = smalloc(sizeof(struct beeptime));
1209 newbeep->ticks = ticks;
1210 newbeep->next = NULL;
1214 beeptail->next = newbeep;
1220 * Throw out any beeps that happened more than
1224 beephead->ticks < ticks - cfg.bellovl_t) {
1225 struct beeptime *tmp = beephead;
1226 beephead = tmp->next;
1233 if (cfg.bellovl && beep_overloaded &&
1234 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1236 * If we're currently overloaded and the
1237 * last beep was more than s seconds ago,
1238 * leave overload mode.
1240 beep_overloaded = FALSE;
1241 } else if (cfg.bellovl && !beep_overloaded &&
1242 nbeeps >= cfg.bellovl_n) {
1244 * Now, if we have n or more beeps
1245 * remaining in the queue, go into overload
1248 beep_overloaded = TRUE;
1253 * Perform an actual beep if we're not overloaded.
1255 if (!cfg.bellovl || !beep_overloaded) {
1257 if (cfg.beep == BELL_VISUAL) {
1259 vbell_startpoint = ticks;
1267 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1268 else if (curs.x == 0 && curs.y > 0)
1269 curs.x = cols - 1, curs.y--;
1275 seen_disp_event = TRUE;
1278 compatibility(VT100);
1282 compatibility(VT100);
1287 termstate = VT52_ESC;
1289 compatibility(ANSIMIN);
1290 termstate = SEEN_ESC;
1298 seen_disp_event = TRUE;
1300 logtraffic((unsigned char) c, LGTYP_ASCII);
1303 if (has_compat(SCOANSI)) {
1305 erase_lots(FALSE, FALSE, TRUE);
1308 seen_disp_event = 1;
1312 compatibility(VT100);
1314 if (curs.y == marg_b)
1315 scroll(marg_t, marg_b, 1, TRUE);
1316 else if (curs.y < rows - 1)
1322 seen_disp_event = 1;
1324 logtraffic((unsigned char) c, LGTYP_ASCII);
1328 pos old_curs = curs;
1329 unsigned long *ldata = lineptr(curs.y);
1333 } while (curs.x < cols - 1 && !tabs[curs.x]);
1335 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1336 if (curs.x >= cols / 2)
1337 curs.x = cols / 2 - 1;
1344 check_selection(old_curs, curs);
1346 seen_disp_event = TRUE;
1350 switch (termstate) {
1352 /* Only graphic characters get this far, ctrls are stripped above */
1353 if (wrapnext && wrap) {
1354 cpos[1] |= LATTR_WRAPPED;
1355 if (curs.y == marg_b)
1356 scroll(marg_t, marg_b, 1, TRUE);
1357 else if (curs.y < rows - 1)
1365 if (selstate != NO_SELECTION) {
1366 pos cursplus = curs;
1368 check_selection(curs, cursplus);
1370 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1371 logtraffic((unsigned char) c, LGTYP_ASCII);
1373 extern int wcwidth(wchar_t ucs);
1378 width = wcwidth((wchar_t) c);
1381 *cpos++ = c | curr_attr;
1382 if (++curs.x == cols) {
1383 *cpos |= LATTR_WRAPPED;
1384 if (curs.y == marg_b)
1385 scroll(marg_t, marg_b, 1, TRUE);
1386 else if (curs.y < rows - 1)
1391 *cpos++ = UCSWIDE | curr_attr;
1394 *cpos++ = c | curr_attr;
1401 if (curs.x == cols) {
1405 if (wrap && vt52_mode) {
1406 cpos[1] |= LATTR_WRAPPED;
1407 if (curs.y == marg_b)
1408 scroll(marg_t, marg_b, 1, TRUE);
1409 else if (curs.y < rows - 1)
1416 seen_disp_event = 1;
1421 * This state is virtually identical to SEEN_ESC, with the
1422 * exception that we have an OSC sequence in the pipeline,
1423 * and _if_ we see a backslash, we process it.
1427 termstate = TOPLEVEL;
1430 /* else fall through */
1432 if (c >= ' ' && c <= '/') {
1439 termstate = TOPLEVEL;
1440 switch (ANSI(c, esc_query)) {
1441 case '[': /* enter CSI mode */
1442 termstate = SEEN_CSI;
1444 esc_args[0] = ARG_DEFAULT;
1447 case ']': /* xterm escape sequences */
1448 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1449 compatibility(OTHER);
1450 termstate = SEEN_OSC;
1453 case '7': /* save cursor */
1454 compatibility(VT100);
1457 case '8': /* restore cursor */
1458 compatibility(VT100);
1460 seen_disp_event = TRUE;
1463 compatibility(VT100);
1464 app_keypad_keys = TRUE;
1467 compatibility(VT100);
1468 app_keypad_keys = FALSE;
1470 case 'D': /* exactly equivalent to LF */
1471 compatibility(VT100);
1472 if (curs.y == marg_b)
1473 scroll(marg_t, marg_b, 1, TRUE);
1474 else if (curs.y < rows - 1)
1478 seen_disp_event = TRUE;
1480 case 'E': /* exactly equivalent to CR-LF */
1481 compatibility(VT100);
1483 if (curs.y == marg_b)
1484 scroll(marg_t, marg_b, 1, TRUE);
1485 else if (curs.y < rows - 1)
1489 seen_disp_event = TRUE;
1491 case 'M': /* reverse index - backwards LF */
1492 compatibility(VT100);
1493 if (curs.y == marg_t)
1494 scroll(marg_t, marg_b, -1, TRUE);
1495 else if (curs.y > 0)
1499 seen_disp_event = TRUE;
1501 case 'Z': /* terminal type query */
1502 compatibility(VT100);
1503 ldisc_send(id_string, strlen(id_string), 0);
1505 case 'c': /* restore power-on settings */
1506 compatibility(VT100);
1509 request_resize(80, rows);
1514 seen_disp_event = TRUE;
1516 case 'H': /* set a tab */
1517 compatibility(VT100);
1518 tabs[curs.x] = TRUE;
1521 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1522 compatibility(VT100);
1524 unsigned long *ldata;
1528 for (i = 0; i < rows; i++) {
1530 for (j = 0; j < cols; j++)
1531 ldata[j] = ATTR_DEFAULT | 'E';
1535 seen_disp_event = TRUE;
1536 scrtop.x = scrtop.y = 0;
1539 check_selection(scrtop, scrbot);
1543 case ANSI('3', '#'):
1544 case ANSI('4', '#'):
1545 case ANSI('5', '#'):
1546 case ANSI('6', '#'):
1547 compatibility(VT100);
1549 unsigned long nlattr;
1550 unsigned long *ldata;
1551 switch (ANSI(c, esc_query)) {
1552 case ANSI('3', '#'):
1555 case ANSI('4', '#'):
1558 case ANSI('5', '#'):
1559 nlattr = LATTR_NORM;
1561 default: /* spiritually case ANSI('6', '#'): */
1562 nlattr = LATTR_WIDE;
1565 ldata = lineptr(curs.y);
1566 ldata[cols] &= ~LATTR_MODE;
1567 ldata[cols] |= nlattr;
1571 case ANSI('A', '('):
1572 compatibility(VT100);
1573 cset_attr[0] = ATTR_GBCHR;
1575 case ANSI('B', '('):
1576 compatibility(VT100);
1577 cset_attr[0] = ATTR_ASCII;
1579 case ANSI('0', '('):
1580 compatibility(VT100);
1581 cset_attr[0] = ATTR_LINEDRW;
1583 case ANSI('U', '('):
1584 compatibility(OTHER);
1585 cset_attr[0] = ATTR_SCOACS;
1588 case ANSI('A', ')'):
1589 compatibility(VT100);
1590 cset_attr[1] = ATTR_GBCHR;
1592 case ANSI('B', ')'):
1593 compatibility(VT100);
1594 cset_attr[1] = ATTR_ASCII;
1596 case ANSI('0', ')'):
1597 compatibility(VT100);
1598 cset_attr[1] = ATTR_LINEDRW;
1600 case ANSI('U', ')'):
1601 compatibility(OTHER);
1602 cset_attr[1] = ATTR_SCOACS;
1605 case ANSI('8', '%'): /* Old Linux code */
1606 case ANSI('G', '%'):
1607 compatibility(OTHER);
1610 case ANSI('@', '%'):
1611 compatibility(OTHER);
1617 termstate = TOPLEVEL; /* default */
1619 if (esc_nargs <= ARGS_MAX) {
1620 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1621 esc_args[esc_nargs - 1] = 0;
1622 esc_args[esc_nargs - 1] =
1623 10 * esc_args[esc_nargs - 1] + c - '0';
1625 termstate = SEEN_CSI;
1626 } else if (c == ';') {
1627 if (++esc_nargs <= ARGS_MAX)
1628 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1629 termstate = SEEN_CSI;
1630 } else if (c < '@') {
1637 termstate = SEEN_CSI;
1639 switch (ANSI(c, esc_query)) {
1640 case 'A': /* move up N lines */
1641 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1642 seen_disp_event = TRUE;
1644 case 'e': /* move down N lines */
1645 compatibility(ANSI);
1648 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1649 seen_disp_event = TRUE;
1651 case ANSI('c', '>'): /* report xterm version */
1652 compatibility(OTHER);
1653 /* this reports xterm version 136 so that VIM can
1654 use the drag messages from the mouse reporting */
1655 ldisc_send("\033[>0;136;0c", 11, 0);
1657 case 'a': /* move right N cols */
1658 compatibility(ANSI);
1661 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1662 seen_disp_event = TRUE;
1664 case 'D': /* move left N cols */
1665 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1666 seen_disp_event = TRUE;
1668 case 'E': /* move down N lines and CR */
1669 compatibility(ANSI);
1670 move(0, curs.y + def(esc_args[0], 1), 1);
1671 seen_disp_event = TRUE;
1673 case 'F': /* move up N lines and CR */
1674 compatibility(ANSI);
1675 move(0, curs.y - def(esc_args[0], 1), 1);
1676 seen_disp_event = TRUE;
1679 case '`': /* set horizontal posn */
1680 compatibility(ANSI);
1681 move(def(esc_args[0], 1) - 1, curs.y, 0);
1682 seen_disp_event = TRUE;
1684 case 'd': /* set vertical posn */
1685 compatibility(ANSI);
1687 (dec_om ? marg_t : 0) + def(esc_args[0],
1690 seen_disp_event = TRUE;
1693 case 'f': /* set horz and vert posns at once */
1695 esc_args[1] = ARG_DEFAULT;
1696 move(def(esc_args[1], 1) - 1,
1697 (dec_om ? marg_t : 0) + def(esc_args[0],
1700 seen_disp_event = TRUE;
1702 case 'J': /* erase screen or parts of it */
1704 unsigned int i = def(esc_args[0], 0) + 1;
1707 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1710 seen_disp_event = TRUE;
1712 case 'K': /* erase line or parts of it */
1714 unsigned int i = def(esc_args[0], 0) + 1;
1717 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1719 seen_disp_event = TRUE;
1721 case 'L': /* insert lines */
1722 compatibility(VT102);
1723 if (curs.y <= marg_b)
1724 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1727 seen_disp_event = TRUE;
1729 case 'M': /* delete lines */
1730 compatibility(VT102);
1731 if (curs.y <= marg_b)
1732 scroll(curs.y, marg_b, def(esc_args[0], 1),
1735 seen_disp_event = TRUE;
1737 case '@': /* insert chars */
1738 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1739 compatibility(VT102);
1740 insch(def(esc_args[0], 1));
1741 seen_disp_event = TRUE;
1743 case 'P': /* delete chars */
1744 compatibility(VT102);
1745 insch(-def(esc_args[0], 1));
1746 seen_disp_event = TRUE;
1748 case 'c': /* terminal type query */
1749 compatibility(VT100);
1750 /* This is the response for a VT102 */
1751 ldisc_send(id_string, strlen(id_string), 0);
1753 case 'n': /* cursor position query */
1754 if (esc_args[0] == 6) {
1756 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1758 ldisc_send(buf, strlen(buf), 0);
1759 } else if (esc_args[0] == 5) {
1760 ldisc_send("\033[0n", 4, 0);
1763 case 'h': /* toggle modes to high */
1765 compatibility(VT100);
1768 for (i = 0; i < esc_nargs; i++)
1769 toggle_mode(esc_args[i], esc_query, TRUE);
1772 case 'l': /* toggle modes to low */
1774 compatibility(VT100);
1777 for (i = 0; i < esc_nargs; i++)
1778 toggle_mode(esc_args[i], esc_query, FALSE);
1781 case 'g': /* clear tabs */
1782 compatibility(VT100);
1783 if (esc_nargs == 1) {
1784 if (esc_args[0] == 0) {
1785 tabs[curs.x] = FALSE;
1786 } else if (esc_args[0] == 3) {
1788 for (i = 0; i < cols; i++)
1793 case 'r': /* set scroll margins */
1794 compatibility(VT100);
1795 if (esc_nargs <= 2) {
1797 top = def(esc_args[0], 1) - 1;
1798 bot = (esc_nargs <= 1
1800 0 ? rows : def(esc_args[1], rows)) - 1;
1803 /* VTTEST Bug 9 - if region is less than 2 lines
1804 * don't change region.
1806 if (bot - top > 0) {
1811 * I used to think the cursor should be
1812 * placed at the top of the newly marginned
1813 * area. Apparently not: VMS TPU falls over
1816 * Well actually it should for Origin mode - RDB
1818 curs.y = (dec_om ? marg_t : 0);
1820 seen_disp_event = TRUE;
1824 case 'm': /* set graphics rendition */
1827 * A VT100 without the AVO only had one attribute, either
1828 * underline or reverse video depending on the cursor type,
1829 * this was selected by CSI 7m.
1832 * This is sometimes DIM, eg on the GIGI and Linux
1834 * This is sometimes INVIS various ANSI.
1836 * This like 22 disables BOLD, DIM and INVIS
1838 * The ANSI colours appear on any terminal that has colour
1839 * (obviously) but the interaction between sgr0 and the
1840 * colours varies but is usually related to the background
1841 * colour erase item.
1842 * The interaction between colour attributes and the mono
1843 * ones is also very implementation dependent.
1845 * The 39 and 49 attributes are likely to be unimplemented.
1848 for (i = 0; i < esc_nargs; i++) {
1849 switch (def(esc_args[i], 0)) {
1850 case 0: /* restore defaults */
1851 curr_attr = ATTR_DEFAULT;
1853 case 1: /* enable bold */
1854 compatibility(VT100AVO);
1855 curr_attr |= ATTR_BOLD;
1857 case 21: /* (enable double underline) */
1858 compatibility(OTHER);
1859 case 4: /* enable underline */
1860 compatibility(VT100AVO);
1861 curr_attr |= ATTR_UNDER;
1863 case 5: /* enable blink */
1864 compatibility(VT100AVO);
1865 curr_attr |= ATTR_BLINK;
1867 case 7: /* enable reverse video */
1868 curr_attr |= ATTR_REVERSE;
1870 case 10: /* SCO acs off */
1871 compatibility(SCOANSI);
1873 case 11: /* SCO acs on */
1874 compatibility(SCOANSI);
1876 case 12: /* SCO acs on flipped */
1877 compatibility(SCOANSI);
1879 case 22: /* disable bold */
1880 compatibility2(OTHER, VT220);
1881 curr_attr &= ~ATTR_BOLD;
1883 case 24: /* disable underline */
1884 compatibility2(OTHER, VT220);
1885 curr_attr &= ~ATTR_UNDER;
1887 case 25: /* disable blink */
1888 compatibility2(OTHER, VT220);
1889 curr_attr &= ~ATTR_BLINK;
1891 case 27: /* disable reverse video */
1892 compatibility2(OTHER, VT220);
1893 curr_attr &= ~ATTR_REVERSE;
1904 curr_attr &= ~ATTR_FGMASK;
1906 (esc_args[i] - 30) << ATTR_FGSHIFT;
1908 case 39: /* default-foreground */
1909 curr_attr &= ~ATTR_FGMASK;
1910 curr_attr |= ATTR_DEFFG;
1921 curr_attr &= ~ATTR_BGMASK;
1923 (esc_args[i] - 40) << ATTR_BGSHIFT;
1925 case 49: /* default-background */
1926 curr_attr &= ~ATTR_BGMASK;
1927 curr_attr |= ATTR_DEFBG;
1932 erase_char = (' ' | ATTR_ASCII |
1934 (ATTR_FGMASK | ATTR_BGMASK)));
1937 case 's': /* save cursor */
1940 case 'u': /* restore cursor */
1942 seen_disp_event = TRUE;
1944 case 't': /* set page size - ie window height */
1946 * VT340/VT420 sequence DECSLPP, DEC only allows values
1947 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1948 * illegal values (eg first arg 1..9) for window changing
1951 compatibility(VT340TEXT);
1953 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1954 request_resize(cols, def(esc_args[0], 24));
1959 compatibility(SCOANSI);
1960 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1963 seen_disp_event = TRUE;
1966 compatibility(SCOANSI);
1967 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1970 seen_disp_event = TRUE;
1972 case ANSI('|', '*'):
1973 /* VT420 sequence DECSNLS
1974 * Set number of lines on screen
1975 * VT420 uses VGA like hardware and can support any size in
1976 * reasonable range (24..49 AIUI) with no default specified.
1978 compatibility(VT420);
1979 if (esc_nargs == 1 && esc_args[0] > 0) {
1980 request_resize(cols, def(esc_args[0], cfg.height));
1984 case ANSI('|', '$'):
1985 /* VT340/VT420 sequence DECSCPP
1986 * Set number of columns per page
1987 * Docs imply range is only 80 or 132, but I'll allow any.
1989 compatibility(VT340TEXT);
1990 if (esc_nargs <= 1) {
1991 request_resize(def(esc_args[0], cfg.width), rows);
1995 case 'X': /* write N spaces w/o moving cursor */
1996 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1997 compatibility(ANSIMIN);
1999 int n = def(esc_args[0], 1);
2001 unsigned long *p = cpos;
2002 if (n > cols - curs.x)
2006 check_selection(curs, cursplus);
2009 seen_disp_event = TRUE;
2012 case 'x': /* report terminal characteristics */
2013 compatibility(VT100);
2016 int i = def(esc_args[0], 0);
2017 if (i == 0 || i == 1) {
2018 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2020 ldisc_send(buf, 20, 0);
2024 case 'Z': /* BackTab for xterm */
2025 compatibility(OTHER);
2027 int i = def(esc_args[0], 1);
2028 pos old_curs = curs;
2030 for(;i>0 && curs.x>0; i--) {
2033 } while (curs.x >0 && !tabs[curs.x]);
2036 check_selection(old_curs, curs);
2039 case ANSI('L', '='):
2040 compatibility(OTHER);
2041 use_bce = (esc_args[0] <= 0);
2042 erase_char = ERASE_CHAR;
2044 erase_char = (' ' | ATTR_ASCII |
2046 (ATTR_FGMASK | ATTR_BGMASK)));
2048 case ANSI('E', '='):
2049 compatibility(OTHER);
2050 blink_is_real = (esc_args[0] >= 1);
2052 case ANSI('p', '"'):
2053 /* Allow the host to make this emulator a 'perfect' VT102.
2054 * This first appeared in the VT220, but we do need to get
2055 * back to PuTTY mode so I won't check it.
2057 * The arg in 40..42,50 are a PuTTY extension.
2058 * The 2nd arg, 8bit vs 7bit is not checked.
2060 * Setting VT102 mode should also change the Fkeys to
2061 * generate PF* codes as a real VT102 has no Fkeys.
2062 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2065 * Note ESC c will NOT change this!
2068 switch (esc_args[0]) {
2070 compatibility_level &= ~TM_VTXXX;
2071 compatibility_level |= TM_VT102;
2074 compatibility_level &= ~TM_VTXXX;
2075 compatibility_level |= TM_VT220;
2079 if (esc_args[0] > 60 && esc_args[0] < 70)
2080 compatibility_level |= TM_VTXXX;
2084 compatibility_level &= TM_VTXXX;
2087 compatibility_level = TM_PUTTY;
2090 compatibility_level = TM_SCOANSI;
2094 compatibility_level = TM_PUTTY;
2100 /* Change the response to CSI c */
2101 if (esc_args[0] == 50) {
2104 strcpy(id_string, "\033[?");
2105 for (i = 1; i < esc_nargs; i++) {
2107 strcat(id_string, ";");
2108 sprintf(lbuf, "%d", esc_args[i]);
2109 strcat(id_string, lbuf);
2111 strcat(id_string, "c");
2114 /* Is this a good idea ?
2115 * Well we should do a soft reset at this point ...
2117 if (!has_compat(VT420) && has_compat(VT100)) {
2119 request_resize(132, 24);
2121 request_resize(80, 24);
2130 case 'P': /* Linux palette sequence */
2131 termstate = SEEN_OSC_P;
2134 case 'R': /* Linux palette reset */
2137 termstate = TOPLEVEL;
2139 case 'W': /* word-set */
2140 termstate = SEEN_OSC_W;
2153 esc_args[0] = 10 * esc_args[0] + c - '0';
2157 * Grotty hack to support xterm and DECterm title
2158 * sequences concurrently.
2160 if (esc_args[0] == 2) {
2164 /* else fall through */
2166 termstate = OSC_STRING;
2172 * This OSC stuff is EVIL. It takes just one character to get into
2173 * sysline mode and it's not initially obvious how to get out.
2174 * So I've added CR and LF as string aborts.
2175 * This shouldn't effect compatibility as I believe embedded
2176 * control characters are supposed to be interpreted (maybe?)
2177 * and they don't display anything useful anyway.
2181 if (c == '\n' || c == '\r') {
2182 termstate = TOPLEVEL;
2183 } else if (c == 0234 || c == '\007') {
2185 * These characters terminate the string; ST and BEL
2186 * terminate the sequence and trigger instant
2187 * processing of it, whereas ESC goes back to SEEN_ESC
2188 * mode unless it is followed by \, in which case it is
2189 * synonymous with ST in the first place.
2192 termstate = TOPLEVEL;
2193 } else if (c == '\033')
2194 termstate = OSC_MAYBE_ST;
2195 else if (osc_strlen < OSC_STR_MAX)
2196 osc_string[osc_strlen++] = c;
2200 int max = (osc_strlen == 0 ? 21 : 16);
2202 if (c >= '0' && c <= '9')
2204 else if (c >= 'A' && c <= 'A' + max - 10)
2206 else if (c >= 'a' && c <= 'a' + max - 10)
2209 termstate = TOPLEVEL;
2212 osc_string[osc_strlen++] = val;
2213 if (osc_strlen >= 7) {
2214 palette_set(osc_string[0],
2215 osc_string[1] * 16 + osc_string[2],
2216 osc_string[3] * 16 + osc_string[4],
2217 osc_string[5] * 16 + osc_string[6]);
2219 termstate = TOPLEVEL;
2235 esc_args[0] = 10 * esc_args[0] + c - '0';
2238 termstate = OSC_STRING;
2243 termstate = TOPLEVEL;
2244 seen_disp_event = TRUE;
2247 move(curs.x, curs.y - 1, 1);
2250 move(curs.x, curs.y + 1, 1);
2253 move(curs.x + 1, curs.y, 1);
2256 move(curs.x - 1, curs.y, 1);
2259 * From the VT100 Manual
2260 * NOTE: The special graphics characters in the VT100
2261 * are different from those in the VT52
2263 * From VT102 manual:
2264 * 137 _ Blank - Same
2265 * 140 ` Reserved - Humm.
2266 * 141 a Solid rectangle - Similar
2267 * 142 b 1/ - Top half of fraction for the
2268 * 143 c 3/ - subscript numbers below.
2271 * 146 f Degrees - Same
2272 * 147 g Plus or minus - Same
2274 * 151 i Ellipsis (dots)
2277 * 154 l Bar at scan 0
2278 * 155 m Bar at scan 1
2279 * 156 n Bar at scan 2
2280 * 157 o Bar at scan 3 - Similar
2281 * 160 p Bar at scan 4 - Similar
2282 * 161 q Bar at scan 5 - Similar
2283 * 162 r Bar at scan 6 - Same
2284 * 163 s Bar at scan 7 - Similar
2299 cset_attr[cset = 0] = ATTR_LINEDRW;
2302 cset_attr[cset = 0] = ATTR_ASCII;
2309 scroll(0, rows - 1, -1, TRUE);
2310 else if (curs.y > 0)
2316 erase_lots(FALSE, FALSE, TRUE);
2320 erase_lots(TRUE, FALSE, TRUE);
2324 /* XXX Print cursor line */
2327 /* XXX Start controller mode */
2330 /* XXX Stop controller mode */
2334 termstate = VT52_Y1;
2337 ldisc_send("\033/Z", 3, 0);
2340 app_keypad_keys = TRUE;
2343 app_keypad_keys = FALSE;
2346 /* XXX This should switch to VT100 mode not current or default
2347 * VT mode. But this will only have effect in a VT220+
2351 blink_is_real = cfg.blinktext;
2355 /* XXX Enter auto print mode */
2358 /* XXX Exit auto print mode */
2361 /* XXX Print screen */
2367 /* compatibility(ATARI) */
2369 erase_lots(FALSE, FALSE, TRUE);
2373 /* compatibility(ATARI) */
2374 if (curs.y <= marg_b)
2375 scroll(curs.y, marg_b, -1, FALSE);
2378 /* compatibility(ATARI) */
2379 if (curs.y <= marg_b)
2380 scroll(curs.y, marg_b, 1, TRUE);
2383 /* compatibility(ATARI) */
2384 termstate = VT52_FG;
2387 /* compatibility(ATARI) */
2388 termstate = VT52_BG;
2391 /* compatibility(ATARI) */
2392 erase_lots(FALSE, TRUE, FALSE);
2396 /* compatibility(ATARI) */
2400 /* compatibility(ATARI) */
2403 /* case 'j': Save cursor position - broken on ST */
2404 /* case 'k': Restore cursor position */
2406 /* compatibility(ATARI) */
2407 erase_lots(TRUE, TRUE, TRUE);
2413 /* compatibility(ATARI) */
2414 erase_lots(TRUE, TRUE, FALSE);
2417 /* compatibility(ATARI) */
2418 curr_attr |= ATTR_REVERSE;
2421 /* compatibility(ATARI) */
2422 curr_attr &= ~ATTR_REVERSE;
2424 case 'v': /* wrap Autowrap on - Wyse style */
2425 /* compatibility(ATARI) */
2428 case 'w': /* Autowrap off */
2429 /* compatibility(ATARI) */
2434 /* compatibility(OTHER) */
2436 curr_attr = ATTR_DEFAULT;
2438 erase_char = (' ' | ATTR_ASCII |
2440 (ATTR_FGMASK | ATTR_BGMASK)));
2443 /* compatibility(VI50) */
2444 curr_attr |= ATTR_UNDER;
2447 /* compatibility(VI50) */
2448 curr_attr &= ~ATTR_UNDER;
2451 /* compatibility(VI50) */
2453 curr_attr |= ATTR_BOLD;
2456 /* compatibility(VI50) */
2458 curr_attr &= ~ATTR_BOLD;
2464 termstate = VT52_Y2;
2465 move(curs.x, c - ' ', 0);
2468 termstate = TOPLEVEL;
2469 move(c - ' ', curs.y, 0);
2474 termstate = TOPLEVEL;
2475 curr_attr &= ~ATTR_FGMASK;
2476 curr_attr &= ~ATTR_BOLD;
2477 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2478 if ((c & 0x8) || vt52_bold)
2479 curr_attr |= ATTR_BOLD;
2482 erase_char = (' ' | ATTR_ASCII |
2483 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2486 termstate = TOPLEVEL;
2487 curr_attr &= ~ATTR_BGMASK;
2488 curr_attr &= ~ATTR_BLINK;
2489 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2491 /* Note: bold background */
2493 curr_attr |= ATTR_BLINK;
2496 erase_char = (' ' | ATTR_ASCII |
2497 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2500 default: break; /* placate gcc warning about enum use */
2502 if (selstate != NO_SELECTION) {
2503 pos cursplus = curs;
2505 check_selection(curs, cursplus);
2512 * Compare two lines to determine whether they are sufficiently
2513 * alike to scroll-optimise one to the other. Return the degree of
2516 static int linecmp(unsigned long *a, unsigned long *b)
2520 for (i = n = 0; i < cols; i++)
2521 n += (*a++ == *b++);
2527 * Given a context, update the window. Out of paranoia, we don't
2528 * allow WM_PAINT responses to do scrolling optimisations.
2530 static void do_paint(Context ctx, int may_optimise)
2532 int i, j, our_curs_y;
2533 unsigned long rv, cursor;
2536 long cursor_background = ERASE_CHAR;
2537 unsigned long ticks;
2540 * Check the visual bell state.
2543 ticks = GetTickCount();
2544 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2548 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2551 * screen array, disptop, scrtop,
2553 * cfg.blinkpc, blink_is_real, tblinker,
2554 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2557 /* Has the cursor position or type changed ? */
2560 if (blinker || !cfg.blink_cur)
2561 cursor = TATTR_ACTCURS;
2565 cursor = TATTR_PASCURS;
2567 cursor |= TATTR_RIGHTCURS;
2570 our_curs_y = curs.y - disptop;
2572 if (dispcurs && (curstype != cursor ||
2574 disptext + our_curs_y * (cols + 1) + curs.x)) {
2575 if (dispcurs > disptext &&
2576 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2577 dispcurs[-1] |= ATTR_INVALID;
2578 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2579 dispcurs[1] |= ATTR_INVALID;
2580 *dispcurs |= ATTR_INVALID;
2585 /* The normal screen data */
2586 for (i = 0; i < rows; i++) {
2587 unsigned long *ldata;
2589 int idx, dirty_line, dirty_run;
2590 unsigned long attr = 0;
2591 int updated_line = 0;
2594 int last_run_dirty = 0;
2596 scrpos.y = i + disptop;
2597 ldata = lineptr(scrpos.y);
2598 lattr = (ldata[cols] & LATTR_MODE);
2600 idx = i * (cols + 1);
2601 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2602 disptext[idx + cols] = ldata[cols];
2604 for (j = 0; j < cols; j++, idx++) {
2605 unsigned long tattr, tchar;
2606 unsigned long *d = ldata + j;
2610 tchar = (*d & (CHAR_MASK | CSET_MASK));
2611 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2612 switch (tchar & CSET_MASK) {
2614 tchar = unitab_line[tchar & 0xFF];
2617 tchar = unitab_xterm[tchar & 0xFF];
2620 tchar = unitab_scoacs[tchar&0xFF];
2623 tattr |= (tchar & CSET_MASK);
2625 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2628 /* Video reversing things */
2630 ^ (posle(selstart, scrpos) &&
2631 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2633 /* 'Real' blinking ? */
2634 if (blink_is_real && (tattr & ATTR_BLINK)) {
2635 if (has_focus && tblinker) {
2637 tattr &= ~CSET_MASK;
2640 tattr &= ~ATTR_BLINK;
2644 * Check the font we'll _probably_ be using to see if
2645 * the character is wide when we don't want it to be.
2647 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2648 if ((tattr & ATTR_WIDE) == 0 &&
2649 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2650 tattr |= ATTR_NARROW;
2651 } else if (disptext[idx]&ATTR_NARROW)
2652 tattr |= ATTR_NARROW;
2654 /* Cursor here ? Save the 'background' */
2655 if (i == our_curs_y && j == curs.x) {
2656 cursor_background = tattr | tchar;
2657 dispcurs = disptext + idx;
2660 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2663 break_run = (tattr != attr || j - start >= sizeof(ch));
2665 /* Special hack for VT100 Linedraw glyphs */
2666 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2667 && tchar <= 0xBD) break_run = TRUE;
2669 if (!dbcs_screenfont && !dirty_line) {
2670 if ((tchar | tattr) == disptext[idx])
2672 else if (!dirty_run && ccount == 1)
2677 if ((dirty_run || last_run_dirty) && ccount > 0) {
2678 do_text(ctx, start, i, ch, ccount, attr, lattr);
2684 if (dbcs_screenfont)
2685 last_run_dirty = dirty_run;
2686 dirty_run = dirty_line;
2689 if ((tchar | tattr) != disptext[idx])
2691 ch[ccount++] = (char) tchar;
2692 disptext[idx] = tchar | tattr;
2694 /* If it's a wide char step along to the next one. */
2695 if (tattr & ATTR_WIDE) {
2699 /* Cursor is here ? Ouch! */
2700 if (i == our_curs_y && j == curs.x) {
2701 cursor_background = *d;
2702 dispcurs = disptext + idx;
2704 if (disptext[idx] != *d)
2710 if (dirty_run && ccount > 0) {
2711 do_text(ctx, start, i, ch, ccount, attr, lattr);
2715 /* Cursor on this line ? (and changed) */
2716 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2717 ch[0] = (char) (cursor_background & CHAR_MASK);
2718 attr = (cursor_background & ATTR_MASK) | cursor;
2719 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2726 * Flick the switch that says if blinking things should be shown or hidden.
2729 void term_blink(int flg)
2731 static long last_blink = 0;
2732 static long last_tblink = 0;
2733 long now, blink_diff;
2735 now = GetTickCount();
2736 blink_diff = now - last_tblink;
2738 /* Make sure the text blinks no more than 2Hz */
2739 if (blink_diff < 0 || blink_diff > 450) {
2741 tblinker = !tblinker;
2750 blink_diff = now - last_blink;
2752 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2753 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2761 * Invalidate the whole screen so it will be repainted in full.
2763 void term_invalidate(void)
2767 for (i = 0; i < rows * (cols + 1); i++)
2768 disptext[i] = ATTR_INVALID;
2772 * Paint the window in response to a WM_PAINT message.
2774 void term_paint(Context ctx, int left, int top, int right, int bottom)
2777 if (left < 0) left = 0;
2778 if (top < 0) top = 0;
2779 if (right >= cols) right = cols-1;
2780 if (bottom >= rows) bottom = rows-1;
2782 for (i = top; i <= bottom && i < rows; i++) {
2783 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2784 for (j = left; j <= right && j < cols; j++)
2785 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2787 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2788 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2791 /* This should happen soon enough, also for some reason it sometimes
2792 * fails to actually do anything when re-sizing ... painting the wrong
2796 do_paint (ctx, FALSE);
2800 * Attempt to scroll the scrollback. The second parameter gives the
2801 * position we want to scroll to; the first is +1 to denote that
2802 * this position is relative to the beginning of the scrollback, -1
2803 * to denote it is relative to the end, and 0 to denote that it is
2804 * relative to the current position.
2806 void term_scroll(int rel, int where)
2808 int sbtop = -count234(scrollback);
2810 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2811 if (disptop < sbtop)
2819 static void clipme(pos top, pos bottom)
2822 wchar_t *wbptr; /* where next char goes within workbuf */
2823 int wblen = 0; /* workbuf len */
2824 int buflen; /* amount of memory allocated to workbuf */
2826 buflen = 5120; /* Default size */
2827 workbuf = smalloc(buflen * sizeof(wchar_t));
2828 wbptr = workbuf; /* start filling here */
2830 while (poslt(top, bottom)) {
2832 unsigned long *ldata = lineptr(top.y);
2838 if (!(ldata[cols] & LATTR_WRAPPED)) {
2839 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2840 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2841 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2842 && poslt(top, nlpos))
2844 if (poslt(nlpos, bottom))
2847 while (poslt(top, bottom) && poslt(top, nlpos)) {
2850 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2852 wchar_t cbuf[16], *p;
2853 int uc = (ldata[top.x] & 0xFFFF);
2856 if (uc == UCSWIDE) {
2861 switch (uc & CSET_MASK) {
2864 uc = unitab_xterm[uc & 0xFF];
2868 uc = unitab_line[uc & 0xFF];
2871 uc = unitab_scoacs[uc&0xFF];
2874 switch (uc & CSET_MASK) {
2876 uc = unitab_font[uc & 0xFF];
2879 uc = unitab_oemcp[uc & 0xFF];
2883 set = (uc & CSET_MASK);
2884 c = (uc & CHAR_MASK);
2888 if (DIRECT_FONT(uc)) {
2889 if (c >= ' ' && c != 0x7F) {
2890 unsigned char buf[4];
2893 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2895 buf[1] = (unsigned char) ldata[top.x + 1];
2896 rv = MultiByteToWideChar(font_codepage,
2897 0, buf, 2, wbuf, 4);
2901 rv = MultiByteToWideChar(font_codepage,
2902 0, buf, 1, wbuf, 4);
2906 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2913 for (p = cbuf; *p; p++) {
2914 /* Enough overhead for trailing NL and nul */
2915 if (wblen >= buflen - 16) {
2918 sizeof(wchar_t) * (buflen += 100));
2919 wbptr = workbuf + wblen;
2928 for (i = 0; i < sel_nl_sz; i++) {
2930 *wbptr++ = sel_nl[i];
2938 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2939 if (buflen > 0) /* indicates we allocated this buffer */
2943 void term_copyall(void)
2946 top.y = -count234(scrollback);
2952 * The wordness array is mainly for deciding the disposition of the US-ASCII
2955 static int wordtype(int uc)
2958 int start, end, ctype;
2959 } *wptr, ucs_words[] = {
2965 0x037e, 0x037e, 1}, /* Greek question mark */
2967 0x0387, 0x0387, 1}, /* Greek ano teleia */
2969 0x055a, 0x055f, 1}, /* Armenian punctuation */
2971 0x0589, 0x0589, 1}, /* Armenian full stop */
2973 0x0700, 0x070d, 1}, /* Syriac punctuation */
2975 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2977 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2979 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2981 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2983 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2985 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2987 0x2000, 0x200a, 0}, /* Various spaces */
2989 0x2070, 0x207f, 2}, /* superscript */
2991 0x2080, 0x208f, 2}, /* subscript */
2993 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2995 0x3000, 0x3000, 0}, /* ideographic space */
2997 0x3001, 0x3020, 1}, /* ideographic punctuation */
2999 0x303f, 0x309f, 3}, /* Hiragana */
3001 0x30a0, 0x30ff, 3}, /* Katakana */
3003 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3005 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3007 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3009 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3011 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3013 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3015 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3017 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3019 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3024 uc &= (CSET_MASK | CHAR_MASK);
3026 switch (uc & CSET_MASK) {
3028 uc = unitab_xterm[uc & 0xFF];
3031 uc = unitab_line[uc & 0xFF];
3034 uc = unitab_scoacs[uc&0xFF];
3037 switch (uc & CSET_MASK) {
3039 uc = unitab_font[uc & 0xFF];
3042 uc = unitab_oemcp[uc & 0xFF];
3046 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3047 * fail as there's such a thing as a double width space. :-(
3049 if (dbcs_screenfont && font_codepage == line_codepage)
3053 return wordness[uc];
3055 for (wptr = ucs_words; wptr->start; wptr++) {
3056 if (uc >= wptr->start && uc <= wptr->end)
3064 * Spread the selection outwards according to the selection mode.
3066 static pos sel_spread_half(pos p, int dir)
3068 unsigned long *ldata;
3071 ldata = lineptr(p.y);
3076 * In this mode, every character is a separate unit, except
3077 * for runs of spaces at the end of a non-wrapping line.
3079 if (!(ldata[cols] & LATTR_WRAPPED)) {
3080 unsigned long *q = ldata + cols;
3081 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3083 if (q == ldata + cols)
3085 if (p.x >= q - ldata)
3086 p.x = (dir == -1 ? q - ldata : cols - 1);
3091 * In this mode, the units are maximal runs of characters
3092 * whose `wordness' has the same value.
3094 wvalue = wordtype(ldata[p.x]);
3096 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3099 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3105 * In this mode, every line is a unit.
3107 p.x = (dir == -1 ? 0 : cols - 1);
3113 static void sel_spread(void)
3115 selstart = sel_spread_half(selstart, -1);
3117 selend = sel_spread_half(selend, +1);
3121 void term_do_paste(void)
3126 get_clip(&data, &len);
3131 sfree(paste_buffer);
3132 paste_pos = paste_hold = paste_len = 0;
3133 paste_buffer = smalloc(len * sizeof(wchar_t));
3136 while (p < data + len) {
3137 while (p < data + len &&
3138 !(p <= data + len - sel_nl_sz &&
3139 !memcmp(p, sel_nl, sizeof(sel_nl))))
3144 for (i = 0; i < p - q; i++) {
3145 paste_buffer[paste_len++] = q[i];
3149 if (p <= data + len - sel_nl_sz &&
3150 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3151 paste_buffer[paste_len++] = '\r';
3157 /* Assume a small paste will be OK in one go. */
3158 if (paste_len < 256) {
3159 luni_send(paste_buffer, paste_len, 0);
3161 sfree(paste_buffer);
3163 paste_pos = paste_hold = paste_len = 0;
3166 get_clip(NULL, NULL);
3169 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3170 int shift, int ctrl)
3173 unsigned long *ldata;
3174 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3178 if (a == MA_DRAG && !raw_mouse)
3183 if (a == MA_DRAG && !raw_mouse)
3196 selpoint.y = y + disptop;
3198 ldata = lineptr(selpoint.y);
3199 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3203 int encstate = 0, r, c;
3205 static int is_down = 0;
3209 encstate = 0x20; /* left button down */
3220 case MBT_WHEEL_DOWN:
3223 default: break; /* placate gcc warning about enum use */
3227 if (xterm_mouse == 1)
3240 default: break; /* placate gcc warning about enum use */
3249 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3250 ldisc_send(abuf, 6, 0);
3254 b = translate_button(b);
3256 if (b == MBT_SELECT && a == MA_CLICK) {
3258 selstate = ABOUT_TO;
3259 selanchor = selpoint;
3261 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3263 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3264 selstate = DRAGGING;
3265 selstart = selanchor = selpoint;
3269 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3270 (b == MBT_EXTEND && a != MA_RELEASE)) {
3271 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3273 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3274 if (posdiff(selpoint, selstart) <
3275 posdiff(selend, selstart) / 2) {
3279 selanchor = selstart;
3281 selstate = DRAGGING;
3283 if (selstate != ABOUT_TO && selstate != DRAGGING)
3284 selanchor = selpoint;
3285 selstate = DRAGGING;
3286 if (poslt(selpoint, selanchor)) {
3287 selstart = selpoint;
3291 selstart = selanchor;
3296 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3297 if (selstate == DRAGGING) {
3299 * We've completed a selection. We now transfer the
3300 * data to the clipboard.
3302 clipme(selstart, selend);
3303 selstate = SELECTED;
3305 selstate = NO_SELECTION;
3306 } else if (b == MBT_PASTE
3307 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3318 sfree(paste_buffer);
3325 static long last_paste = 0;
3326 long now, paste_diff;
3331 /* Don't wait forever to paste */
3333 now = GetTickCount();
3334 paste_diff = now - last_paste;
3335 if (paste_diff >= 0 && paste_diff < 450)
3340 while (paste_pos < paste_len) {
3342 while (n + paste_pos < paste_len) {
3343 if (paste_buffer[paste_pos + n++] == '\r')
3346 luni_send(paste_buffer + paste_pos, n, 0);
3349 if (paste_pos < paste_len) {
3354 sfree(paste_buffer);
3359 static void deselect(void)
3361 selstate = NO_SELECTION;
3362 selstart.x = selstart.y = selend.x = selend.y = 0;
3365 void term_deselect(void)
3371 int term_ldisc(int option)
3373 if (option == LD_ECHO)
3374 return term_echoing;
3375 if (option == LD_EDIT)
3376 return term_editing;
3381 * from_backend(), to get data from the backend for the terminal.
3383 int from_backend(int is_stderr, char *data, int len)
3385 bufchain_add(&inbuf, data, len);
3388 * term_out() always completely empties inbuf. Therefore,
3389 * there's no reason at all to return anything other than zero
3390 * from this function, because there _can't_ be a question of
3391 * the remote side needing to wait until term_out() has cleared
3394 * This is a slightly suboptimal way to deal with SSH2 - in
3395 * principle, the window mechanism would allow us to continue
3396 * to accept data on forwarded ports and X connections even
3397 * while the terminal processing was going slowly - but we
3398 * can't do the 100% right thing without moving the terminal
3399 * processing into a separate thread, and that might hurt
3400 * portability. So we manage stdout buffering the old SSH1 way:
3401 * if the terminal processing goes slowly, the whole SSH
3402 * connection stops accepting data until it's ready.
3404 * In practice, I can't imagine this causing serious trouble.
3410 * Log session traffic.
3412 void logtraffic(unsigned char c, int logmode)
3414 if (cfg.logtype > 0) {
3415 if (cfg.logtype == logmode) {
3416 /* deferred open file from pgm start? */
3425 void settimstr(char *ta, int no_sec);
3426 char *subslfcode(char *dest, char *src, char *dstrt);
3427 char *stpncpy(char *dst, const char *src, size_t maxlen);
3429 char currlogfilename[FILENAME_MAX];
3431 /* open log file append/overwrite mode */
3441 sprintf(writemod, "wb"); /* default to rewrite */
3444 tm = *localtime(&t);
3446 /* substitute special codes in file name */
3447 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3449 lgfp = fopen(currlogfilename, "r"); /* file already present? */
3453 i = askappend(currlogfilename);
3455 writemod[0] = 'a'; /* set append mode */
3456 else if (i == 0) { /* cancelled */
3458 cfg.logtype = 0; /* disable logging */
3463 lgfp = fopen(currlogfilename, writemod);
3464 if (lgfp) { /* enter into event log */
3465 sprintf(buf, "%s session log (%s mode) to file : ",
3466 (writemod[0] == 'a') ? "Appending" : "Writing new",
3467 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3468 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3469 /* Make sure we do not exceed the output buffer size */
3470 strncat(buf, currlogfilename, 128);
3471 buf[strlen(buf)] = '\0';
3474 /* --- write header line into log file */
3475 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3476 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
3478 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3482 void logfclose(void)
3491 * translate format codes into time/date strings
3492 * and insert them into log file name
3494 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3496 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3497 char buf[10], *bufp;
3499 char *ds = d; /* save start pos. */
3500 int len = FILENAME_MAX-1;
3503 /* Let (bufp, len) be the string to append. */
3504 bufp = buf; /* don't usually override this */
3508 if (*s) switch (c = *s++, tolower(c)) {
3510 size = strftime(buf, sizeof(buf), "%Y", tm);
3513 size = strftime(buf, sizeof(buf), "%m", tm);
3516 size = strftime(buf, sizeof(buf), "%d", tm);
3519 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3523 size = strlen(bufp);
3537 memcpy(d, bufp, size);