14 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15 #define CL_VT100 0x0002 /* VT100 */
16 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102 0x0008 /* VT102 */
18 #define CL_VT220 0x0010 /* VT220 */
19 #define CL_VT320 0x0020 /* VT320 */
20 #define CL_VT420 0x0040 /* VT420 */
21 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
27 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29 #define TM_VT102 (TM_VT100AVO|CL_VT102)
30 #define TM_VT220 (TM_VT102|CL_VT220)
31 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level = TM_PUTTY;
51 static tree234 *scrollback; /* lines scrolled off top of screen */
52 static tree234 *screen; /* lines on primary screen */
53 static tree234 *alt_screen; /* lines on alternate screen */
54 static int disptop; /* distance scrolled back (0 or -ve) */
56 static unsigned long *cpos; /* cursor position (convenience) */
58 static unsigned long *disptext; /* buffer of text on real screen */
59 static unsigned long *dispcurs; /* location of cursor on real screen */
60 static unsigned long curstype; /* type of cursor on real screen */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime *next;
68 static struct beeptime *beephead, *beeptail;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89 static pos curs; /* cursor */
90 static pos savecurs; /* saved cursor position */
91 static int marg_t, marg_b; /* scroll margins */
92 static int dec_om; /* DEC origin mode flag */
93 static int wrap, wrapnext; /* wrap flags */
94 static int insert; /* insert-mode flag */
95 static int cset; /* 0 or 1: which char set */
96 static int save_cset, save_csattr; /* saved with cursor position */
97 static int save_utf; /* saved with cursor position */
98 static int rvideo; /* global reverse video flag */
99 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
100 static int cursor_on; /* cursor enabled flag */
101 static int reset_132; /* Flag ESC c resets to 80 cols */
102 static int use_bce; /* Use Background coloured erase */
103 static int blinker; /* When blinking is the cursor on ? */
104 static int tblinker; /* When the blinking text is on */
105 static int blink_is_real; /* Actually blink blinking text */
106 static int term_echoing; /* Does terminal want local echo? */
107 static int term_editing; /* Does terminal want local edit? */
108 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
109 static int vt52_bold; /* Force bold on non-bold colours */
110 static int utf_state; /* Is there a pending UTF-8 character */
111 static int utf_char; /* and what is it so far. */
112 static int utf_size; /* The size of the UTF character. */
114 static int xterm_mouse; /* send mouse messages to app */
116 static unsigned long cset_attr[2];
119 * Saved settings on the alternate screen.
121 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
122 static int alt_t, alt_b;
123 static int alt_which;
125 #define ARGS_MAX 32 /* max # of esc sequence arguments */
126 #define ARG_DEFAULT 0 /* if an arg isn't specified */
127 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
128 static int esc_args[ARGS_MAX];
129 static int esc_nargs;
130 static int esc_query;
131 #define ANSI(x,y) ((x)+((y)<<8))
132 #define ANSI_QUE(x) ANSI(x,TRUE)
134 #define OSC_STR_MAX 2048
135 static int osc_strlen;
136 static char osc_string[OSC_STR_MAX + 1];
139 static char id_string[1024] = "\033[?6c";
141 static unsigned char *tabs;
153 OSC_STRING, OSC_MAYBE_ST,
162 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
165 SM_CHAR, SM_WORD, SM_LINE
167 static pos selstart, selend, selanchor;
169 static short wordness[256] = {
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
172 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
176 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
177 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
184 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
185 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
188 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
189 static wchar_t sel_nl[] = SEL_NL;
190 static wchar_t *paste_buffer = 0;
191 static int paste_len, paste_pos, paste_hold;
194 * Internal prototypes.
196 static void do_paint(Context, int);
197 static void erase_lots(int, int, int);
198 static void swap_screen(int);
199 static void update_sbar(void);
200 static void deselect(void);
201 /* log session to file stuff ... */
202 static FILE *lgfp = NULL;
203 static void logtraffic(unsigned char c, int logmode);
204 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
207 * Resize a line to make it `cols' columns wide.
209 unsigned long *resizeline(unsigned long *line, int cols)
212 unsigned long lineattrs;
214 if (line[0] != (unsigned long)cols) {
216 * This line is the wrong length, which probably means it
217 * hasn't been accessed since a resize. Resize it now.
220 lineattrs = line[oldlen + 1];
221 line = srealloc(line, TSIZE * (2 + cols));
223 for (i = oldlen; i < cols; i++)
224 line[i + 1] = ERASE_CHAR;
225 line[cols + 1] = lineattrs & LATTR_MODE;
232 * Retrieve a line of the screen or of the scrollback, according to
233 * whether the y coordinate is non-negative or negative
236 unsigned long *lineptr(int y, int lineno)
238 unsigned long *line, *newline;
246 whichtree = scrollback;
247 treeindex = y + count234(scrollback);
249 line = index234(whichtree, treeindex);
251 /* We assume that we don't screw up and retrieve something out of range. */
252 assert(line != NULL);
254 newline = resizeline(line, cols);
255 if (newline != line) {
256 delpos234(whichtree, treeindex);
257 addpos234(whichtree, newline, treeindex);
264 #define lineptr(x) lineptr(x,__LINE__)
266 * Set up power-on settings for the terminal.
268 static void power_on(void)
270 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
273 alt_b = marg_b = rows - 1;
278 for (i = 0; i < cols; i++)
279 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
281 alt_om = dec_om = cfg.dec_om;
282 alt_wnext = wrapnext = alt_ins = insert = FALSE;
283 alt_wrap = wrap = cfg.wrap_mode;
286 alt_sco_acs = sco_acs = 0;
287 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
292 save_attr = curr_attr = ATTR_DEFAULT;
293 term_editing = term_echoing = FALSE;
294 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
295 app_cursor_keys = cfg.app_cursor;
296 app_keypad_keys = cfg.app_keypad;
298 blink_is_real = cfg.blinktext;
299 erase_char = ERASE_CHAR;
303 for (i = 0; i < 256; i++)
304 wordness[i] = cfg.wordness[i];
308 erase_lots(FALSE, TRUE, TRUE);
310 erase_lots(FALSE, TRUE, TRUE);
315 * Force a screen update.
317 void term_update(void)
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;
330 sys_cursor(curs.x, curs.y - disptop);
336 * Same as power_on(), but an external function.
338 void term_pwron(void)
348 * Clear the scrollback.
350 void term_clrsb(void)
354 while ((line = delpos234(scrollback, 0)) != NULL) {
361 * Initialise the terminal.
365 screen = alt_screen = scrollback = NULL;
367 disptext = dispcurs = NULL;
372 beephead = beeptail = NULL;
375 beep_overloaded = FALSE;
379 * Set up the terminal for a given size.
381 void term_size(int newrows, int newcols, int newsavelines)
384 unsigned long *newdisp, *line;
387 int save_alt_which = alt_which;
389 if (newrows == rows && newcols == cols && newsavelines == savelines)
390 return; /* nothing to do */
396 alt_b = marg_b = newrows - 1;
399 scrollback = newtree234(NULL);
400 screen = newtree234(NULL);
405 * Resize the screen and scrollback. We only need to shift
406 * lines around within our data structures, because lineptr()
407 * will take care of resizing each individual line if
410 * - If the new screen and the old screen differ in length, we
411 * must shunt some lines in from the scrollback or out to
414 * - If doing that fails to provide us with enough material to
415 * fill the new screen (i.e. the number of rows needed in
416 * the new screen exceeds the total number in the previous
417 * screen+scrollback), we must invent some blank lines to
420 * - Then, if the new scrollback length is less than the
421 * amount of scrollback we actually have, we must throw some
424 sblen = count234(scrollback);
425 /* Do this loop to expand the screen if newrows > rows */
426 for (i = rows; i < newrows; i++) {
428 line = delpos234(scrollback, --sblen);
430 line = smalloc(TSIZE * (newcols + 2));
432 for (j = 0; j <= newcols; j++)
433 line[j + 1] = ERASE_CHAR;
435 addpos234(screen, line, 0);
437 /* Do this loop to shrink the screen if newrows < rows */
438 for (i = newrows; i < rows; i++) {
439 line = delpos234(screen, 0);
440 addpos234(scrollback, line, sblen++);
442 assert(count234(screen) == newrows);
443 while (sblen > newsavelines) {
444 line = delpos234(scrollback, 0);
448 assert(count234(scrollback) <= newsavelines);
451 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
452 for (i = 0; i < newrows * (newcols + 1); i++)
453 newdisp[i] = ATTR_INVALID;
458 newalt = newtree234(NULL);
459 for (i = 0; i < newrows; i++) {
460 line = smalloc(TSIZE * (newcols + 2));
462 for (j = 0; j <= newcols; j++)
463 line[j + 1] = erase_char;
464 addpos234(newalt, line, i);
467 while (NULL != (line = delpos234(alt_screen, 0)))
469 freetree234(alt_screen);
473 tabs = srealloc(tabs, newcols * sizeof(*tabs));
476 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
477 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
481 curs.y += newrows - rows;
484 if (curs.y >= newrows)
485 curs.y = newrows - 1;
486 if (curs.x >= newcols)
487 curs.x = newcols - 1;
489 wrapnext = alt_wnext = FALSE;
493 savelines = newsavelines;
496 swap_screen(save_alt_which);
506 static void swap_screen(int which)
511 if (which == alt_which)
538 wrapnext = alt_wnext;
550 sco_acs = alt_sco_acs;
557 * Update the scroll bar.
559 static void update_sbar(void)
563 nscroll = count234(scrollback);
565 set_sbar(nscroll + rows, nscroll + disptop, rows);
569 * Check whether the region bounded by the two pointers intersects
570 * the scroll region, and de-select the on-screen selection if so.
572 static void check_selection(pos from, pos to)
574 if (poslt(from, selend) && poslt(selstart, to))
579 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
580 * for backward.) `sb' is TRUE if the scrolling is permitted to
581 * affect the scrollback buffer.
583 * NB this function invalidates all pointers into lines of the
584 * screen data structures. In particular, you MUST call fix_cpos
585 * after calling scroll() and before doing anything else that
586 * uses the cpos shortcut pointer.
588 static void scroll(int topline, int botline, int lines, int sb)
590 unsigned long *line, *line2;
593 if (topline != 0 || alt_which != 0)
598 line = delpos234(screen, botline);
599 line = resizeline(line, cols);
600 for (i = 0; i < cols; i++)
601 line[i + 1] = erase_char;
603 addpos234(screen, line, topline);
605 if (selstart.y >= topline && selstart.y <= botline) {
607 if (selstart.y > botline) {
608 selstart.y = botline;
612 if (selend.y >= topline && selend.y <= botline) {
614 if (selend.y > botline) {
624 line = delpos234(screen, topline);
625 if (sb && savelines > 0) {
626 int sblen = count234(scrollback);
628 * We must add this line to the scrollback. We'll
629 * remove a line from the top of the scrollback to
630 * replace it, or allocate a new one if the
631 * scrollback isn't full.
633 if (sblen == savelines) {
634 sblen--, line2 = delpos234(scrollback, 0);
636 line2 = smalloc(TSIZE * (cols + 2));
639 addpos234(scrollback, line, sblen);
643 * If the user is currently looking at part of the
644 * scrollback, and they haven't enabled any options
645 * that are going to reset the scrollback as a
646 * result of this movement, then the chances are
647 * they'd like to keep looking at the same line. So
648 * we move their viewpoint at the same rate as the
649 * scroll, at least until their viewpoint hits the
650 * top end of the scrollback buffer, at which point
651 * we don't have the choice any more.
653 * Thanks to Jan Holmen Holsten for the idea and
654 * initial implementation.
656 if (disptop > -savelines && disptop < 0)
659 line = resizeline(line, cols);
660 for (i = 0; i < cols; i++)
661 line[i + 1] = erase_char;
663 addpos234(screen, line, botline);
666 * If the selection endpoints move into the scrollback,
667 * we keep them moving until they hit the top. However,
668 * of course, if the line _hasn't_ moved into the
669 * scrollback then we don't do this, and cut them off
670 * at the top of the scroll region.
672 seltop = sb ? -savelines : 0;
674 if (selstart.y >= seltop && selstart.y <= botline) {
676 if (selstart.y < seltop) {
681 if (selend.y >= seltop && selend.y <= botline) {
683 if (selend.y < seltop) {
695 * Move the cursor to a given position, clipping at boundaries. We
696 * may or may not want to clip at the scroll margin: marg_clip is 0
697 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
698 * even _being_ outside the margins.
700 static void move(int x, int y, int marg_clip)
707 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
709 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
723 * Save or restore the cursor and SGR mode.
725 static void save_cursor(int save)
729 save_attr = curr_attr;
732 save_csattr = cset_attr[cset];
733 save_sco_acs = sco_acs;
736 /* Make sure the window hasn't shrunk since the save */
742 curr_attr = save_attr;
745 cset_attr[cset] = save_csattr;
746 sco_acs = save_sco_acs;
749 erase_char = (' ' | ATTR_ASCII |
750 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
755 * Erase a large portion of the screen: the whole screen, or the
756 * whole line, or parts thereof.
758 static void erase_lots(int line_only, int from_begin, int to_end)
762 unsigned long *ldata;
784 check_selection(start, end);
786 /* Clear screen also forces a full window redraw, just in case. */
787 if (start.y == 0 && start.x == 0 && end.y == rows)
790 ldata = lineptr(start.y);
791 while (poslt(start, end)) {
792 if (start.x == cols && !erase_lattr)
793 ldata[start.x] &= ~LATTR_WRAPPED;
795 ldata[start.x] = erase_char;
796 if (incpos(start) && start.y < rows)
797 ldata = lineptr(start.y);
802 * Insert or delete characters within the current line. n is +ve if
803 * insertion is desired, and -ve for deletion.
805 static void insch(int n)
807 int dir = (n < 0 ? -1 : +1);
810 unsigned long *ldata;
812 n = (n < 0 ? -n : n);
813 if (n > cols - curs.x)
815 m = cols - curs.x - n;
817 cursplus.x = curs.x + n;
818 check_selection(curs, cursplus);
819 ldata = lineptr(curs.y);
821 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
823 ldata[curs.x + m++] = erase_char;
825 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
827 ldata[curs.x + n] = erase_char;
832 * Toggle terminal mode `mode' to state `state'. (`query' indicates
833 * whether the mode is a DEC private one or a normal one.)
835 static void toggle_mode(int mode, int query, int state)
841 case 1: /* application cursor keys */
842 app_cursor_keys = state;
844 case 2: /* VT52 mode */
847 blink_is_real = FALSE;
850 blink_is_real = cfg.blinktext;
853 case 3: /* 80/132 columns */
855 request_resize(state ? 132 : 80, rows);
858 case 5: /* reverse video */
860 * Toggle reverse video. If we receive an OFF within the
861 * visual bell timeout period after an ON, we trigger an
862 * effective visual bell, so that ESC[?5hESC[?5l will
863 * always be an actually _visible_ visual bell.
865 ticks = GetTickCount();
866 if (rvideo && !state && /* we're turning it off */
867 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
868 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
869 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
870 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
871 } else if (!rvideo && state) {
872 /* This is an ON, so we notice the time and save it. */
873 rvbell_timeout = ticks + VBELL_TIMEOUT;
876 seen_disp_event = TRUE;
880 case 6: /* DEC origin mode */
883 case 7: /* auto wrap */
886 case 8: /* auto key repeat */
889 case 10: /* set local edit mode */
890 term_editing = state;
891 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
893 case 25: /* enable/disable cursor */
894 compatibility2(OTHER, VT220);
896 seen_disp_event = TRUE;
898 case 47: /* alternate screen */
899 compatibility(OTHER);
904 case 1000: /* xterm mouse 1 */
905 xterm_mouse = state ? 1 : 0;
906 set_raw_mouse_mode(state);
908 case 1002: /* xterm mouse 2 */
909 xterm_mouse = state ? 2 : 0;
910 set_raw_mouse_mode(state);
914 case 4: /* set insert mode */
915 compatibility(VT102);
918 case 12: /* set echo mode */
919 term_echoing = !state;
920 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
922 case 20: /* Return sends ... */
923 cr_lf_return = state;
925 case 34: /* Make cursor BIG */
926 compatibility2(OTHER, VT220);
932 * Process an OSC sequence: set window title or icon name.
934 static void do_osc(void)
938 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
940 osc_string[osc_strlen] = '\0';
941 switch (esc_args[0]) {
944 set_icon(osc_string);
945 if (esc_args[0] == 1)
947 /* fall through: parameter 0 means set both */
950 set_title(osc_string);
957 * Remove everything currently in `inbuf' and stick it up on the
958 * in-memory display. There's a big state machine in here to
959 * process escape sequences...
966 * Optionally log the session traffic to a file. Useful for
967 * debugging and possibly also useful for actual logging.
969 if (cfg.logtype == LGTYP_DEBUG)
970 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
971 logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
974 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
975 c = inbuf[inbuf_reap];
977 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
978 * be able to display 8-bit characters, but I'll let that go 'cause
982 /* First see about all those translations. */
983 if (termstate == TOPLEVEL) {
988 /* UTF-8 must be stateless so we ignore iso2022. */
989 if (unitab_ctrl[c] != 0xFF)
991 else c = ((unsigned char)c) | ATTR_ASCII;
993 } else if ((c & 0xe0) == 0xc0) {
994 utf_size = utf_state = 1;
995 utf_char = (c & 0x1f);
996 } else if ((c & 0xf0) == 0xe0) {
997 utf_size = utf_state = 2;
998 utf_char = (c & 0x0f);
999 } else if ((c & 0xf8) == 0xf0) {
1000 utf_size = utf_state = 3;
1001 utf_char = (c & 0x07);
1002 } else if ((c & 0xfc) == 0xf8) {
1003 utf_size = utf_state = 4;
1004 utf_char = (c & 0x03);
1005 } else if ((c & 0xfe) == 0xfc) {
1006 utf_size = utf_state = 5;
1007 utf_char = (c & 0x01);
1018 if ((c & 0xC0) != 0x80) {
1024 utf_char = (utf_char << 6) | (c & 0x3f);
1030 /* Is somebody trying to be evil! */
1032 (c < 0x800 && utf_size >= 2) ||
1033 (c < 0x10000 && utf_size >= 3) ||
1034 (c < 0x200000 && utf_size >= 4) ||
1035 (c < 0x4000000 && utf_size >= 5))
1038 /* Unicode line separator and paragraph separator are CR-LF */
1039 if (c == 0x2028 || c == 0x2029)
1042 /* High controls are probably a Baaad idea too. */
1046 /* The UTF-16 surrogates are not nice either. */
1047 /* The standard give the option of decoding these:
1048 * I don't want to! */
1049 if (c >= 0xD800 && c < 0xE000)
1052 /* ISO 10646 characters now limited to UTF-16 range. */
1056 /* This is currently a TagPhobic application.. */
1057 if (c >= 0xE0000 && c <= 0xE007F)
1060 /* U+FEFF is best seen as a null. */
1063 /* But U+FFFE is an error. */
1064 if (c == 0xFFFE || c == 0xFFFF)
1067 /* Oops this is a 16bit implementation */
1072 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1074 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1076 if (sco_acs == 2) c ^= 0x80;
1079 switch (cset_attr[cset]) {
1081 * Linedraw characters are different from 'ESC ( B'
1082 * only for a small range. For ones outside that
1083 * range, make sure we use the same font as well as
1084 * the same encoding.
1087 if (unitab_ctrl[c] != 0xFF)
1090 c = ((unsigned char) c) | ATTR_LINEDRW;
1094 /* If UK-ASCII, make the '#' a LineDraw Pound */
1096 c = '}' | ATTR_LINEDRW;
1099 /*FALLTHROUGH*/ case ATTR_ASCII:
1100 if (unitab_ctrl[c] != 0xFF)
1103 c = ((unsigned char) c) | ATTR_ASCII;
1106 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1112 /* How about C1 controls ? */
1113 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1114 has_compat(VT220)) {
1115 termstate = SEEN_ESC;
1117 c = '@' + (c & 0x1F);
1120 /* Or the GL control. */
1121 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1122 if (curs.x && !wrapnext)
1126 *cpos = (' ' | curr_attr | ATTR_ASCII);
1128 /* Or normal C0 controls. */
1129 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1131 case '\005': /* terminal type query */
1132 /* Strictly speaking this is VT100 but a VT100 defaults to
1133 * no response. Other terminals respond at their option.
1135 * Don't put a CR in the default string as this tends to
1136 * upset some weird software.
1138 * An xterm returns "xterm" (5 characters)
1140 compatibility(ANSIMIN);
1142 char abuf[256], *s, *d;
1144 for (s = cfg.answerback, d = abuf; *s; s++) {
1146 if (*s >= 'a' && *s <= 'z')
1147 *d++ = (*s - ('a' - 1));
1148 else if ((*s >= '@' && *s <= '_') ||
1149 *s == '?' || (*s & 0x80))
1154 } else if (*s == '^') {
1159 lpage_send(CP_ACP, abuf, d - abuf);
1164 struct beeptime *newbeep;
1167 ticks = GetTickCount();
1169 if (!beep_overloaded) {
1170 newbeep = smalloc(sizeof(struct beeptime));
1171 newbeep->ticks = ticks;
1172 newbeep->next = NULL;
1176 beeptail->next = newbeep;
1182 * Throw out any beeps that happened more than
1186 beephead->ticks < ticks - cfg.bellovl_t) {
1187 struct beeptime *tmp = beephead;
1188 beephead = tmp->next;
1195 if (cfg.bellovl && beep_overloaded &&
1196 ticks - lastbeep >= cfg.bellovl_s) {
1198 * If we're currently overloaded and the
1199 * last beep was more than s seconds ago,
1200 * leave overload mode.
1202 beep_overloaded = FALSE;
1203 } else if (cfg.bellovl && !beep_overloaded &&
1204 nbeeps >= cfg.bellovl_n) {
1206 * Now, if we have n or more beeps
1207 * remaining in the queue, go into overload
1210 beep_overloaded = TRUE;
1215 * Perform an actual beep if we're not overloaded.
1217 if (!cfg.bellovl || !beep_overloaded) {
1219 if (cfg.beep == BELL_VISUAL) {
1221 vbell_timeout = ticks + VBELL_TIMEOUT;
1229 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1230 else if (curs.x == 0 && curs.y > 0)
1231 curs.x = cols - 1, curs.y--;
1237 seen_disp_event = TRUE;
1240 compatibility(VT100);
1244 compatibility(VT100);
1249 termstate = VT52_ESC;
1251 compatibility(ANSIMIN);
1252 termstate = SEEN_ESC;
1260 seen_disp_event = TRUE;
1262 logtraffic((unsigned char) c, LGTYP_ASCII);
1265 if (has_compat(SCOANSI)) {
1267 erase_lots(FALSE, FALSE, TRUE);
1270 seen_disp_event = 1;
1274 compatibility(VT100);
1276 if (curs.y == marg_b)
1277 scroll(marg_t, marg_b, 1, TRUE);
1278 else if (curs.y < rows - 1)
1284 seen_disp_event = 1;
1286 logtraffic((unsigned char) c, LGTYP_ASCII);
1290 pos old_curs = curs;
1291 unsigned long *ldata = lineptr(curs.y);
1295 } while (curs.x < cols - 1 && !tabs[curs.x]);
1297 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1298 if (curs.x >= cols / 2)
1299 curs.x = cols / 2 - 1;
1306 check_selection(old_curs, curs);
1308 seen_disp_event = TRUE;
1312 switch (termstate) {
1314 /* Only graphic characters get this far, ctrls are stripped above */
1315 if (wrapnext && wrap) {
1316 cpos[1] |= LATTR_WRAPPED;
1317 if (curs.y == marg_b)
1318 scroll(marg_t, marg_b, 1, TRUE);
1319 else if (curs.y < rows - 1)
1327 if (selstate != NO_SELECTION) {
1328 pos cursplus = curs;
1330 check_selection(curs, cursplus);
1332 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1333 logtraffic((unsigned char) c, LGTYP_ASCII);
1335 extern int wcwidth(wchar_t ucs);
1340 width = wcwidth((wchar_t) c);
1343 *cpos++ = c | curr_attr;
1344 if (++curs.x == cols) {
1345 *cpos |= LATTR_WRAPPED;
1346 if (curs.y == marg_b)
1347 scroll(marg_t, marg_b, 1, TRUE);
1348 else if (curs.y < rows - 1)
1353 *cpos++ = UCSWIDE | curr_attr;
1356 *cpos++ = c | curr_attr;
1363 if (curs.x == cols) {
1367 if (wrap && vt52_mode) {
1368 cpos[1] |= LATTR_WRAPPED;
1369 if (curs.y == marg_b)
1370 scroll(marg_t, marg_b, 1, TRUE);
1371 else if (curs.y < rows - 1)
1378 seen_disp_event = 1;
1383 * This state is virtually identical to SEEN_ESC, with the
1384 * exception that we have an OSC sequence in the pipeline,
1385 * and _if_ we see a backslash, we process it.
1389 termstate = TOPLEVEL;
1392 /* else fall through */
1394 if (c >= ' ' && c <= '/') {
1401 termstate = TOPLEVEL;
1402 switch (ANSI(c, esc_query)) {
1403 case '[': /* enter CSI mode */
1404 termstate = SEEN_CSI;
1406 esc_args[0] = ARG_DEFAULT;
1409 case ']': /* xterm escape sequences */
1410 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1411 compatibility(OTHER);
1412 termstate = SEEN_OSC;
1415 case '7': /* save cursor */
1416 compatibility(VT100);
1419 case '8': /* restore cursor */
1420 compatibility(VT100);
1422 seen_disp_event = TRUE;
1425 compatibility(VT100);
1426 app_keypad_keys = TRUE;
1429 compatibility(VT100);
1430 app_keypad_keys = FALSE;
1432 case 'D': /* exactly equivalent to LF */
1433 compatibility(VT100);
1434 if (curs.y == marg_b)
1435 scroll(marg_t, marg_b, 1, TRUE);
1436 else if (curs.y < rows - 1)
1440 seen_disp_event = TRUE;
1442 case 'E': /* exactly equivalent to CR-LF */
1443 compatibility(VT100);
1445 if (curs.y == marg_b)
1446 scroll(marg_t, marg_b, 1, TRUE);
1447 else if (curs.y < rows - 1)
1451 seen_disp_event = TRUE;
1453 case 'M': /* reverse index - backwards LF */
1454 compatibility(VT100);
1455 if (curs.y == marg_t)
1456 scroll(marg_t, marg_b, -1, TRUE);
1457 else if (curs.y > 0)
1461 seen_disp_event = TRUE;
1463 case 'Z': /* terminal type query */
1464 compatibility(VT100);
1465 ldisc_send(id_string, strlen(id_string));
1467 case 'c': /* restore power-on settings */
1468 compatibility(VT100);
1471 request_resize(80, rows);
1476 seen_disp_event = TRUE;
1478 case 'H': /* set a tab */
1479 compatibility(VT100);
1480 tabs[curs.x] = TRUE;
1483 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1484 compatibility(VT100);
1486 unsigned long *ldata;
1490 for (i = 0; i < rows; i++) {
1492 for (j = 0; j < cols; j++)
1493 ldata[j] = ATTR_DEFAULT | 'E';
1497 seen_disp_event = TRUE;
1498 scrtop.x = scrtop.y = 0;
1501 check_selection(scrtop, scrbot);
1505 case ANSI('3', '#'):
1506 case ANSI('4', '#'):
1507 case ANSI('5', '#'):
1508 case ANSI('6', '#'):
1509 compatibility(VT100);
1511 unsigned long nlattr;
1512 unsigned long *ldata;
1513 switch (ANSI(c, esc_query)) {
1514 case ANSI('3', '#'):
1517 case ANSI('4', '#'):
1520 case ANSI('5', '#'):
1521 nlattr = LATTR_NORM;
1523 default: /* spiritually case ANSI('6', '#'): */
1524 nlattr = LATTR_WIDE;
1527 ldata = lineptr(curs.y);
1528 ldata[cols] &= ~LATTR_MODE;
1529 ldata[cols] |= nlattr;
1533 case ANSI('A', '('):
1534 compatibility(VT100);
1535 cset_attr[0] = ATTR_GBCHR;
1537 case ANSI('B', '('):
1538 compatibility(VT100);
1539 cset_attr[0] = ATTR_ASCII;
1541 case ANSI('0', '('):
1542 compatibility(VT100);
1543 cset_attr[0] = ATTR_LINEDRW;
1545 case ANSI('U', '('):
1546 compatibility(OTHER);
1547 cset_attr[0] = ATTR_SCOACS;
1550 case ANSI('A', ')'):
1551 compatibility(VT100);
1552 cset_attr[1] = ATTR_GBCHR;
1554 case ANSI('B', ')'):
1555 compatibility(VT100);
1556 cset_attr[1] = ATTR_ASCII;
1558 case ANSI('0', ')'):
1559 compatibility(VT100);
1560 cset_attr[1] = ATTR_LINEDRW;
1562 case ANSI('U', ')'):
1563 compatibility(OTHER);
1564 cset_attr[1] = ATTR_SCOACS;
1567 case ANSI('8', '%'): /* Old Linux code */
1568 case ANSI('G', '%'):
1569 compatibility(OTHER);
1572 case ANSI('@', '%'):
1573 compatibility(OTHER);
1579 termstate = TOPLEVEL; /* default */
1581 if (esc_nargs <= ARGS_MAX) {
1582 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1583 esc_args[esc_nargs - 1] = 0;
1584 esc_args[esc_nargs - 1] =
1585 10 * esc_args[esc_nargs - 1] + c - '0';
1587 termstate = SEEN_CSI;
1588 } else if (c == ';') {
1589 if (++esc_nargs <= ARGS_MAX)
1590 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1591 termstate = SEEN_CSI;
1592 } else if (c < '@') {
1599 termstate = SEEN_CSI;
1601 switch (ANSI(c, esc_query)) {
1602 case 'A': /* move up N lines */
1603 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1604 seen_disp_event = TRUE;
1606 case 'e': /* move down N lines */
1607 compatibility(ANSI);
1610 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1611 seen_disp_event = TRUE;
1613 case ANSI('c', '>'): /* report xterm version */
1614 compatibility(OTHER);
1615 /* this reports xterm version 136 so that VIM can
1616 use the drag messages from the mouse reporting */
1617 ldisc_send("\033[>0;136;0c", 11);
1619 case 'a': /* move right N cols */
1620 compatibility(ANSI);
1623 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1624 seen_disp_event = TRUE;
1626 case 'D': /* move left N cols */
1627 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1628 seen_disp_event = TRUE;
1630 case 'E': /* move down N lines and CR */
1631 compatibility(ANSI);
1632 move(0, curs.y + def(esc_args[0], 1), 1);
1633 seen_disp_event = TRUE;
1635 case 'F': /* move up N lines and CR */
1636 compatibility(ANSI);
1637 move(0, curs.y - def(esc_args[0], 1), 1);
1638 seen_disp_event = TRUE;
1641 case '`': /* set horizontal posn */
1642 compatibility(ANSI);
1643 move(def(esc_args[0], 1) - 1, curs.y, 0);
1644 seen_disp_event = TRUE;
1646 case 'd': /* set vertical posn */
1647 compatibility(ANSI);
1649 (dec_om ? marg_t : 0) + def(esc_args[0],
1652 seen_disp_event = TRUE;
1655 case 'f': /* set horz and vert posns at once */
1657 esc_args[1] = ARG_DEFAULT;
1658 move(def(esc_args[1], 1) - 1,
1659 (dec_om ? marg_t : 0) + def(esc_args[0],
1662 seen_disp_event = TRUE;
1664 case 'J': /* erase screen or parts of it */
1666 unsigned int i = def(esc_args[0], 0) + 1;
1669 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1672 seen_disp_event = TRUE;
1674 case 'K': /* erase line or parts of it */
1676 unsigned int i = def(esc_args[0], 0) + 1;
1679 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1681 seen_disp_event = TRUE;
1683 case 'L': /* insert lines */
1684 compatibility(VT102);
1685 if (curs.y <= marg_b)
1686 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1689 seen_disp_event = TRUE;
1691 case 'M': /* delete lines */
1692 compatibility(VT102);
1693 if (curs.y <= marg_b)
1694 scroll(curs.y, marg_b, def(esc_args[0], 1),
1697 seen_disp_event = TRUE;
1699 case '@': /* insert chars */
1700 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1701 compatibility(VT102);
1702 insch(def(esc_args[0], 1));
1703 seen_disp_event = TRUE;
1705 case 'P': /* delete chars */
1706 compatibility(VT102);
1707 insch(-def(esc_args[0], 1));
1708 seen_disp_event = TRUE;
1710 case 'c': /* terminal type query */
1711 compatibility(VT100);
1712 /* This is the response for a VT102 */
1713 ldisc_send(id_string, strlen(id_string));
1715 case 'n': /* cursor position query */
1716 if (esc_args[0] == 6) {
1718 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1720 ldisc_send(buf, strlen(buf));
1721 } else if (esc_args[0] == 5) {
1722 ldisc_send("\033[0n", 4);
1725 case 'h': /* toggle modes to high */
1727 compatibility(VT100);
1730 for (i = 0; i < esc_nargs; i++)
1731 toggle_mode(esc_args[i], esc_query, TRUE);
1734 case 'l': /* toggle modes to low */
1736 compatibility(VT100);
1739 for (i = 0; i < esc_nargs; i++)
1740 toggle_mode(esc_args[i], esc_query, FALSE);
1743 case 'g': /* clear tabs */
1744 compatibility(VT100);
1745 if (esc_nargs == 1) {
1746 if (esc_args[0] == 0) {
1747 tabs[curs.x] = FALSE;
1748 } else if (esc_args[0] == 3) {
1750 for (i = 0; i < cols; i++)
1755 case 'r': /* set scroll margins */
1756 compatibility(VT100);
1757 if (esc_nargs <= 2) {
1759 top = def(esc_args[0], 1) - 1;
1760 bot = (esc_nargs <= 1
1762 0 ? rows : def(esc_args[1], rows)) - 1;
1765 /* VTTEST Bug 9 - if region is less than 2 lines
1766 * don't change region.
1768 if (bot - top > 0) {
1773 * I used to think the cursor should be
1774 * placed at the top of the newly marginned
1775 * area. Apparently not: VMS TPU falls over
1778 * Well actually it should for Origin mode - RDB
1780 curs.y = (dec_om ? marg_t : 0);
1782 seen_disp_event = TRUE;
1786 case 'm': /* set graphics rendition */
1789 * A VT100 without the AVO only had one attribute, either
1790 * underline or reverse video depending on the cursor type,
1791 * this was selected by CSI 7m.
1794 * This is sometimes DIM, eg on the GIGI and Linux
1796 * This is sometimes INVIS various ANSI.
1798 * This like 22 disables BOLD, DIM and INVIS
1800 * The ANSI colours appear on any terminal that has colour
1801 * (obviously) but the interaction between sgr0 and the
1802 * colours varies but is usually related to the background
1803 * colour erase item.
1804 * The interaction between colour attributes and the mono
1805 * ones is also very implementation dependent.
1807 * The 39 and 49 attributes are likely to be unimplemented.
1810 for (i = 0; i < esc_nargs; i++) {
1811 switch (def(esc_args[i], 0)) {
1812 case 0: /* restore defaults */
1813 curr_attr = ATTR_DEFAULT;
1815 case 1: /* enable bold */
1816 compatibility(VT100AVO);
1817 curr_attr |= ATTR_BOLD;
1819 case 21: /* (enable double underline) */
1820 compatibility(OTHER);
1821 case 4: /* enable underline */
1822 compatibility(VT100AVO);
1823 curr_attr |= ATTR_UNDER;
1825 case 5: /* enable blink */
1826 compatibility(VT100AVO);
1827 curr_attr |= ATTR_BLINK;
1829 case 7: /* enable reverse video */
1830 curr_attr |= ATTR_REVERSE;
1832 case 10: /* SCO acs off */
1833 compatibility(SCOANSI);
1835 case 11: /* SCO acs on */
1836 compatibility(SCOANSI);
1838 case 12: /* SCO acs on flipped */
1839 compatibility(SCOANSI);
1841 case 22: /* disable bold */
1842 compatibility2(OTHER, VT220);
1843 curr_attr &= ~ATTR_BOLD;
1845 case 24: /* disable underline */
1846 compatibility2(OTHER, VT220);
1847 curr_attr &= ~ATTR_UNDER;
1849 case 25: /* disable blink */
1850 compatibility2(OTHER, VT220);
1851 curr_attr &= ~ATTR_BLINK;
1853 case 27: /* disable reverse video */
1854 compatibility2(OTHER, VT220);
1855 curr_attr &= ~ATTR_REVERSE;
1866 curr_attr &= ~ATTR_FGMASK;
1868 (esc_args[i] - 30) << ATTR_FGSHIFT;
1870 case 39: /* default-foreground */
1871 curr_attr &= ~ATTR_FGMASK;
1872 curr_attr |= ATTR_DEFFG;
1883 curr_attr &= ~ATTR_BGMASK;
1885 (esc_args[i] - 40) << ATTR_BGSHIFT;
1887 case 49: /* default-background */
1888 curr_attr &= ~ATTR_BGMASK;
1889 curr_attr |= ATTR_DEFBG;
1894 erase_char = (' ' | ATTR_ASCII |
1896 (ATTR_FGMASK | ATTR_BGMASK)));
1899 case 's': /* save cursor */
1902 case 'u': /* restore cursor */
1904 seen_disp_event = TRUE;
1906 case 't': /* set page size - ie window height */
1908 * VT340/VT420 sequence DECSLPP, DEC only allows values
1909 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1910 * illegal values (eg first arg 1..9) for window changing
1913 compatibility(VT340TEXT);
1915 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1916 request_resize(cols, def(esc_args[0], 24));
1921 compatibility(SCOANSI);
1922 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1925 seen_disp_event = TRUE;
1928 compatibility(SCOANSI);
1929 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1932 seen_disp_event = TRUE;
1934 case ANSI('|', '*'):
1935 /* VT420 sequence DECSNLS
1936 * Set number of lines on screen
1937 * VT420 uses VGA like hardware and can support any size in
1938 * reasonable range (24..49 AIUI) with no default specified.
1940 compatibility(VT420);
1941 if (esc_nargs == 1 && esc_args[0] > 0) {
1942 request_resize(cols, def(esc_args[0], cfg.height));
1946 case ANSI('|', '$'):
1947 /* VT340/VT420 sequence DECSCPP
1948 * Set number of columns per page
1949 * Docs imply range is only 80 or 132, but I'll allow any.
1951 compatibility(VT340TEXT);
1952 if (esc_nargs <= 1) {
1953 request_resize(def(esc_args[0], cfg.width), rows);
1957 case 'X': /* write N spaces w/o moving cursor */
1958 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1959 compatibility(ANSIMIN);
1961 int n = def(esc_args[0], 1);
1963 unsigned long *p = cpos;
1964 if (n > cols - curs.x)
1968 check_selection(curs, cursplus);
1971 seen_disp_event = TRUE;
1974 case 'x': /* report terminal characteristics */
1975 compatibility(VT100);
1978 int i = def(esc_args[0], 0);
1979 if (i == 0 || i == 1) {
1980 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1982 ldisc_send(buf, 20);
1986 case 'Z': /* BackTab for xterm */
1987 compatibility(OTHER);
1989 int i = def(esc_args[0], 1);
1990 pos old_curs = curs;
1992 for(;i>0 && curs.x>0; i--) {
1995 } while (curs.x >0 && !tabs[curs.x]);
1998 check_selection(old_curs, curs);
2001 case ANSI('L', '='):
2002 compatibility(OTHER);
2003 use_bce = (esc_args[0] <= 0);
2004 erase_char = ERASE_CHAR;
2006 erase_char = (' ' | ATTR_ASCII |
2008 (ATTR_FGMASK | ATTR_BGMASK)));
2010 case ANSI('E', '='):
2011 compatibility(OTHER);
2012 blink_is_real = (esc_args[0] >= 1);
2014 case ANSI('p', '"'):
2015 /* Allow the host to make this emulator a 'perfect' VT102.
2016 * This first appeared in the VT220, but we do need to get
2017 * back to PuTTY mode so I won't check it.
2019 * The arg in 40..42,50 are a PuTTY extension.
2020 * The 2nd arg, 8bit vs 7bit is not checked.
2022 * Setting VT102 mode should also change the Fkeys to
2023 * generate PF* codes as a real VT102 has no Fkeys.
2024 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2027 * Note ESC c will NOT change this!
2030 switch (esc_args[0]) {
2032 compatibility_level &= ~TM_VTXXX;
2033 compatibility_level |= TM_VT102;
2036 compatibility_level &= ~TM_VTXXX;
2037 compatibility_level |= TM_VT220;
2041 if (esc_args[0] > 60 && esc_args[0] < 70)
2042 compatibility_level |= TM_VTXXX;
2046 compatibility_level &= TM_VTXXX;
2049 compatibility_level = TM_PUTTY;
2052 compatibility_level = TM_SCOANSI;
2056 compatibility_level = TM_PUTTY;
2062 /* Change the response to CSI c */
2063 if (esc_args[0] == 50) {
2066 strcpy(id_string, "\033[?");
2067 for (i = 1; i < esc_nargs; i++) {
2069 strcat(id_string, ";");
2070 sprintf(lbuf, "%d", esc_args[i]);
2071 strcat(id_string, lbuf);
2073 strcat(id_string, "c");
2076 /* Is this a good idea ?
2077 * Well we should do a soft reset at this point ...
2079 if (!has_compat(VT420) && has_compat(VT100)) {
2081 request_resize(132, 24);
2083 request_resize(80, 24);
2092 case 'P': /* Linux palette sequence */
2093 termstate = SEEN_OSC_P;
2096 case 'R': /* Linux palette reset */
2099 termstate = TOPLEVEL;
2101 case 'W': /* word-set */
2102 termstate = SEEN_OSC_W;
2115 esc_args[0] = 10 * esc_args[0] + c - '0';
2119 * Grotty hack to support xterm and DECterm title
2120 * sequences concurrently.
2122 if (esc_args[0] == 2) {
2126 /* else fall through */
2128 termstate = OSC_STRING;
2134 * This OSC stuff is EVIL. It takes just one character to get into
2135 * sysline mode and it's not initially obvious how to get out.
2136 * So I've added CR and LF as string aborts.
2137 * This shouldn't effect compatibility as I believe embedded
2138 * control characters are supposed to be interpreted (maybe?)
2139 * and they don't display anything useful anyway.
2143 if (c == '\n' || c == '\r') {
2144 termstate = TOPLEVEL;
2145 } else if (c == 0234 || c == '\007') {
2147 * These characters terminate the string; ST and BEL
2148 * terminate the sequence and trigger instant
2149 * processing of it, whereas ESC goes back to SEEN_ESC
2150 * mode unless it is followed by \, in which case it is
2151 * synonymous with ST in the first place.
2154 termstate = TOPLEVEL;
2155 } else if (c == '\033')
2156 termstate = OSC_MAYBE_ST;
2157 else if (osc_strlen < OSC_STR_MAX)
2158 osc_string[osc_strlen++] = c;
2162 int max = (osc_strlen == 0 ? 21 : 16);
2164 if (c >= '0' && c <= '9')
2166 else if (c >= 'A' && c <= 'A' + max - 10)
2168 else if (c >= 'a' && c <= 'a' + max - 10)
2171 termstate = TOPLEVEL;
2174 osc_string[osc_strlen++] = val;
2175 if (osc_strlen >= 7) {
2176 palette_set(osc_string[0],
2177 osc_string[1] * 16 + osc_string[2],
2178 osc_string[3] * 16 + osc_string[4],
2179 osc_string[5] * 16 + osc_string[6]);
2181 termstate = TOPLEVEL;
2197 esc_args[0] = 10 * esc_args[0] + c - '0';
2200 termstate = OSC_STRING;
2205 termstate = TOPLEVEL;
2206 seen_disp_event = TRUE;
2209 move(curs.x, curs.y - 1, 1);
2212 move(curs.x, curs.y + 1, 1);
2215 move(curs.x + 1, curs.y, 1);
2218 move(curs.x - 1, curs.y, 1);
2221 * From the VT100 Manual
2222 * NOTE: The special graphics characters in the VT100
2223 * are different from those in the VT52
2225 * From VT102 manual:
2226 * 137 _ Blank - Same
2227 * 140 ` Reserved - Humm.
2228 * 141 a Solid rectangle - Similar
2229 * 142 b 1/ - Top half of fraction for the
2230 * 143 c 3/ - subscript numbers below.
2233 * 146 f Degrees - Same
2234 * 147 g Plus or minus - Same
2236 * 151 i Ellipsis (dots)
2239 * 154 l Bar at scan 0
2240 * 155 m Bar at scan 1
2241 * 156 n Bar at scan 2
2242 * 157 o Bar at scan 3 - Similar
2243 * 160 p Bar at scan 4 - Similar
2244 * 161 q Bar at scan 5 - Similar
2245 * 162 r Bar at scan 6 - Same
2246 * 163 s Bar at scan 7 - Similar
2261 cset_attr[cset = 0] = ATTR_LINEDRW;
2264 cset_attr[cset = 0] = ATTR_ASCII;
2271 scroll(0, rows - 1, -1, TRUE);
2272 else if (curs.y > 0)
2278 erase_lots(FALSE, FALSE, TRUE);
2282 erase_lots(TRUE, FALSE, TRUE);
2286 /* XXX Print cursor line */
2289 /* XXX Start controller mode */
2292 /* XXX Stop controller mode */
2296 termstate = VT52_Y1;
2299 ldisc_send("\033/Z", 3);
2302 app_keypad_keys = TRUE;
2305 app_keypad_keys = FALSE;
2308 /* XXX This should switch to VT100 mode not current or default
2309 * VT mode. But this will only have effect in a VT220+
2313 blink_is_real = cfg.blinktext;
2317 /* XXX Enter auto print mode */
2320 /* XXX Exit auto print mode */
2323 /* XXX Print screen */
2329 /* compatibility(ATARI) */
2331 erase_lots(FALSE, FALSE, TRUE);
2335 /* compatibility(ATARI) */
2336 if (curs.y <= marg_b)
2337 scroll(curs.y, marg_b, -1, FALSE);
2340 /* compatibility(ATARI) */
2341 if (curs.y <= marg_b)
2342 scroll(curs.y, marg_b, 1, TRUE);
2345 /* compatibility(ATARI) */
2346 termstate = VT52_FG;
2349 /* compatibility(ATARI) */
2350 termstate = VT52_BG;
2353 /* compatibility(ATARI) */
2354 erase_lots(FALSE, TRUE, FALSE);
2358 /* compatibility(ATARI) */
2362 /* compatibility(ATARI) */
2365 /* case 'j': Save cursor position - broken on ST */
2366 /* case 'k': Restore cursor position */
2368 /* compatibility(ATARI) */
2369 erase_lots(TRUE, TRUE, TRUE);
2375 /* compatibility(ATARI) */
2376 erase_lots(TRUE, TRUE, FALSE);
2379 /* compatibility(ATARI) */
2380 curr_attr |= ATTR_REVERSE;
2383 /* compatibility(ATARI) */
2384 curr_attr &= ~ATTR_REVERSE;
2386 case 'v': /* wrap Autowrap on - Wyse style */
2387 /* compatibility(ATARI) */
2390 case 'w': /* Autowrap off */
2391 /* compatibility(ATARI) */
2396 /* compatibility(OTHER) */
2398 curr_attr = ATTR_DEFAULT;
2400 erase_char = (' ' | ATTR_ASCII |
2402 (ATTR_FGMASK | ATTR_BGMASK)));
2405 /* compatibility(VI50) */
2406 curr_attr |= ATTR_UNDER;
2409 /* compatibility(VI50) */
2410 curr_attr &= ~ATTR_UNDER;
2413 /* compatibility(VI50) */
2415 curr_attr |= ATTR_BOLD;
2418 /* compatibility(VI50) */
2420 curr_attr &= ~ATTR_BOLD;
2426 termstate = VT52_Y2;
2427 move(curs.x, c - ' ', 0);
2430 termstate = TOPLEVEL;
2431 move(c - ' ', curs.y, 0);
2436 termstate = TOPLEVEL;
2437 curr_attr &= ~ATTR_FGMASK;
2438 curr_attr &= ~ATTR_BOLD;
2439 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2440 if ((c & 0x8) || vt52_bold)
2441 curr_attr |= ATTR_BOLD;
2444 erase_char = (' ' | ATTR_ASCII |
2445 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2448 termstate = TOPLEVEL;
2449 curr_attr &= ~ATTR_BGMASK;
2450 curr_attr &= ~ATTR_BLINK;
2451 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2453 /* Note: bold background */
2455 curr_attr |= ATTR_BLINK;
2458 erase_char = (' ' | ATTR_ASCII |
2459 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2462 default: break; /* placate gcc warning about enum use */
2464 if (selstate != NO_SELECTION) {
2465 pos cursplus = curs;
2467 check_selection(curs, cursplus);
2475 * Compare two lines to determine whether they are sufficiently
2476 * alike to scroll-optimise one to the other. Return the degree of
2479 static int linecmp(unsigned long *a, unsigned long *b)
2483 for (i = n = 0; i < cols; i++)
2484 n += (*a++ == *b++);
2490 * Given a context, update the window. Out of paranoia, we don't
2491 * allow WM_PAINT responses to do scrolling optimisations.
2493 static void do_paint(Context ctx, int may_optimise)
2495 int i, j, our_curs_y;
2496 unsigned long rv, cursor;
2499 long cursor_background = ERASE_CHAR;
2503 * Check the visual bell state.
2506 ticks = GetTickCount();
2507 if (ticks - vbell_timeout >= 0)
2511 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2514 * screen array, disptop, scrtop,
2516 * cfg.blinkpc, blink_is_real, tblinker,
2517 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2520 /* Has the cursor position or type changed ? */
2523 if (blinker || !cfg.blink_cur)
2524 cursor = TATTR_ACTCURS;
2528 cursor = TATTR_PASCURS;
2530 cursor |= TATTR_RIGHTCURS;
2533 our_curs_y = curs.y - disptop;
2535 if (dispcurs && (curstype != cursor ||
2537 disptext + our_curs_y * (cols + 1) + curs.x)) {
2538 if (dispcurs > disptext &&
2539 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2540 dispcurs[-1] |= ATTR_INVALID;
2541 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2542 dispcurs[1] |= ATTR_INVALID;
2543 *dispcurs |= ATTR_INVALID;
2548 /* The normal screen data */
2549 for (i = 0; i < rows; i++) {
2550 unsigned long *ldata;
2552 int idx, dirty_line, dirty_run;
2553 unsigned long attr = 0;
2554 int updated_line = 0;
2557 int last_run_dirty = 0;
2559 scrpos.y = i + disptop;
2560 ldata = lineptr(scrpos.y);
2561 lattr = (ldata[cols] & LATTR_MODE);
2563 idx = i * (cols + 1);
2564 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2565 disptext[idx + cols] = ldata[cols];
2567 for (j = 0; j < cols; j++, idx++) {
2568 unsigned long tattr, tchar;
2569 unsigned long *d = ldata + j;
2573 tchar = (*d & (CHAR_MASK | CSET_MASK));
2574 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2575 switch (tchar & CSET_MASK) {
2577 tchar = unitab_line[tchar & 0xFF];
2580 tchar = unitab_xterm[tchar & 0xFF];
2583 tchar = unitab_scoacs[tchar&0xFF];
2586 tattr |= (tchar & CSET_MASK);
2588 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2591 /* Video reversing things */
2593 ^ (posle(selstart, scrpos) &&
2594 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2596 /* 'Real' blinking ? */
2597 if (blink_is_real && (tattr & ATTR_BLINK)) {
2598 if (has_focus && tblinker) {
2600 tattr &= ~CSET_MASK;
2603 tattr &= ~ATTR_BLINK;
2607 * Check the font we'll _probably_ be using to see if
2608 * the character is wide when we don't want it to be.
2610 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2611 if ((tattr & ATTR_WIDE) == 0 &&
2612 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2613 tattr |= ATTR_NARROW;
2614 } else if (disptext[idx]&ATTR_NARROW)
2615 tattr |= ATTR_NARROW;
2617 /* Cursor here ? Save the 'background' */
2618 if (i == our_curs_y && j == curs.x) {
2619 cursor_background = tattr | tchar;
2620 dispcurs = disptext + idx;
2623 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2626 break_run = (tattr != attr || j - start >= sizeof(ch));
2628 /* Special hack for VT100 Linedraw glyphs */
2629 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2630 && tchar <= 0xBD) break_run = TRUE;
2632 if (!dbcs_screenfont && !dirty_line) {
2633 if ((tchar | tattr) == disptext[idx])
2635 else if (!dirty_run && ccount == 1)
2640 if ((dirty_run || last_run_dirty) && ccount > 0) {
2641 do_text(ctx, start, i, ch, ccount, attr, lattr);
2647 if (dbcs_screenfont)
2648 last_run_dirty = dirty_run;
2649 dirty_run = dirty_line;
2652 if ((tchar | tattr) != disptext[idx])
2654 ch[ccount++] = (char) tchar;
2655 disptext[idx] = tchar | tattr;
2657 /* If it's a wide char step along to the next one. */
2658 if (tattr & ATTR_WIDE) {
2662 /* Cursor is here ? Ouch! */
2663 if (i == our_curs_y && j == curs.x) {
2664 cursor_background = *d;
2665 dispcurs = disptext + idx;
2667 if (disptext[idx] != *d)
2673 if (dirty_run && ccount > 0) {
2674 do_text(ctx, start, i, ch, ccount, attr, lattr);
2678 /* Cursor on this line ? (and changed) */
2679 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2680 ch[0] = (char) (cursor_background & CHAR_MASK);
2681 attr = (cursor_background & ATTR_MASK) | cursor;
2682 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2689 * Flick the switch that says if blinking things should be shown or hidden.
2692 void term_blink(int flg)
2694 static long last_blink = 0;
2695 static long last_tblink = 0;
2696 long now, blink_diff;
2698 now = GetTickCount();
2699 blink_diff = now - last_tblink;
2701 /* Make sure the text blinks no more than 2Hz */
2702 if (blink_diff < 0 || blink_diff > 450) {
2704 tblinker = !tblinker;
2713 blink_diff = now - last_blink;
2715 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2716 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2724 * Invalidate the whole screen so it will be repainted in full.
2726 void term_invalidate(void)
2730 for (i = 0; i < rows * (cols + 1); i++)
2731 disptext[i] = ATTR_INVALID;
2735 * Paint the window in response to a WM_PAINT message.
2737 void term_paint(Context ctx, int left, int top, int right, int bottom)
2740 if (left < 0) left = 0;
2741 if (top < 0) top = 0;
2742 if (right >= cols) right = cols-1;
2743 if (bottom >= rows) bottom = rows-1;
2745 for (i = top; i <= bottom && i < rows; i++) {
2746 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2747 for (j = left; j <= right && j < cols; j++)
2748 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2750 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2751 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2754 /* This should happen soon enough, also for some reason it sometimes
2755 * fails to actually do anything when re-sizing ... painting the wrong
2759 do_paint (ctx, FALSE);
2763 * Attempt to scroll the scrollback. The second parameter gives the
2764 * position we want to scroll to; the first is +1 to denote that
2765 * this position is relative to the beginning of the scrollback, -1
2766 * to denote it is relative to the end, and 0 to denote that it is
2767 * relative to the current position.
2769 void term_scroll(int rel, int where)
2771 int sbtop = -count234(scrollback);
2773 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2774 if (disptop < sbtop)
2782 static void clipme(pos top, pos bottom)
2785 wchar_t *wbptr; /* where next char goes within workbuf */
2786 int wblen = 0; /* workbuf len */
2787 int buflen; /* amount of memory allocated to workbuf */
2789 buflen = 5120; /* Default size */
2790 workbuf = smalloc(buflen * sizeof(wchar_t));
2791 wbptr = workbuf; /* start filling here */
2793 while (poslt(top, bottom)) {
2795 unsigned long *ldata = lineptr(top.y);
2801 if (!(ldata[cols] & LATTR_WRAPPED)) {
2802 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2803 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2804 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2805 && poslt(top, nlpos))
2807 if (poslt(nlpos, bottom))
2810 while (poslt(top, bottom) && poslt(top, nlpos)) {
2813 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2815 wchar_t cbuf[16], *p;
2816 int uc = (ldata[top.x] & 0xFFFF);
2819 if (uc == UCSWIDE) {
2824 switch (uc & CSET_MASK) {
2827 uc = unitab_xterm[uc & 0xFF];
2831 uc = unitab_line[uc & 0xFF];
2834 uc = unitab_scoacs[uc&0xFF];
2837 switch (uc & CSET_MASK) {
2839 uc = unitab_font[uc & 0xFF];
2842 uc = unitab_oemcp[uc & 0xFF];
2846 set = (uc & CSET_MASK);
2847 c = (uc & CHAR_MASK);
2851 if (DIRECT_FONT(uc)) {
2852 if (c >= ' ' && c != 0x7F) {
2853 unsigned char buf[4];
2856 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2858 buf[1] = (unsigned char) ldata[top.x + 1];
2859 rv = MultiByteToWideChar(font_codepage,
2860 0, buf, 2, wbuf, 4);
2864 rv = MultiByteToWideChar(font_codepage,
2865 0, buf, 1, wbuf, 4);
2869 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2876 for (p = cbuf; *p; p++) {
2877 /* Enough overhead for trailing NL and nul */
2878 if (wblen >= buflen - 16) {
2881 sizeof(wchar_t) * (buflen += 100));
2882 wbptr = workbuf + wblen;
2891 for (i = 0; i < sel_nl_sz; i++) {
2893 *wbptr++ = sel_nl[i];
2901 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2902 if (buflen > 0) /* indicates we allocated this buffer */
2906 void term_copyall(void)
2909 top.y = -count234(scrollback);
2915 * The wordness array is mainly for deciding the disposition of the US-ASCII
2918 static int wordtype(int uc)
2921 int start, end, ctype;
2922 } *wptr, ucs_words[] = {
2928 0x037e, 0x037e, 1}, /* Greek question mark */
2930 0x0387, 0x0387, 1}, /* Greek ano teleia */
2932 0x055a, 0x055f, 1}, /* Armenian punctuation */
2934 0x0589, 0x0589, 1}, /* Armenian full stop */
2936 0x0700, 0x070d, 1}, /* Syriac punctuation */
2938 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2940 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2942 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2944 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2946 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2948 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2950 0x2000, 0x200a, 0}, /* Various spaces */
2952 0x2070, 0x207f, 2}, /* superscript */
2954 0x2080, 0x208f, 2}, /* subscript */
2956 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2958 0x3000, 0x3000, 0}, /* ideographic space */
2960 0x3001, 0x3020, 1}, /* ideographic punctuation */
2962 0x303f, 0x309f, 3}, /* Hiragana */
2964 0x30a0, 0x30ff, 3}, /* Katakana */
2966 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2968 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2970 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2972 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2974 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2976 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2978 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2980 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2982 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2987 uc &= (CSET_MASK | CHAR_MASK);
2989 switch (uc & CSET_MASK) {
2991 uc = unitab_xterm[uc & 0xFF];
2994 uc = unitab_line[uc & 0xFF];
2997 uc = unitab_scoacs[uc&0xFF];
3000 switch (uc & CSET_MASK) {
3002 uc = unitab_font[uc & 0xFF];
3005 uc = unitab_oemcp[uc & 0xFF];
3009 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3010 * fail as there's such a thing as a double width space. :-(
3012 if (dbcs_screenfont && font_codepage == line_codepage)
3016 return wordness[uc];
3018 for (wptr = ucs_words; wptr->start; wptr++) {
3019 if (uc >= wptr->start && uc <= wptr->end)
3027 * Spread the selection outwards according to the selection mode.
3029 static pos sel_spread_half(pos p, int dir)
3031 unsigned long *ldata;
3034 ldata = lineptr(p.y);
3039 * In this mode, every character is a separate unit, except
3040 * for runs of spaces at the end of a non-wrapping line.
3042 if (!(ldata[cols] & LATTR_WRAPPED)) {
3043 unsigned long *q = ldata + cols;
3044 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3046 if (q == ldata + cols)
3048 if (p.x >= q - ldata)
3049 p.x = (dir == -1 ? q - ldata : cols - 1);
3054 * In this mode, the units are maximal runs of characters
3055 * whose `wordness' has the same value.
3057 wvalue = wordtype(ldata[p.x]);
3059 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3062 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3068 * In this mode, every line is a unit.
3070 p.x = (dir == -1 ? 0 : cols - 1);
3076 static void sel_spread(void)
3078 selstart = sel_spread_half(selstart, -1);
3080 selend = sel_spread_half(selend, +1);
3084 void term_do_paste(void)
3089 get_clip(&data, &len);
3094 sfree(paste_buffer);
3095 paste_pos = paste_hold = paste_len = 0;
3096 paste_buffer = smalloc(len * sizeof(wchar_t));
3099 while (p < data + len) {
3100 while (p < data + len &&
3101 !(p <= data + len - sel_nl_sz &&
3102 !memcmp(p, sel_nl, sizeof(sel_nl))))
3107 for (i = 0; i < p - q; i++) {
3108 paste_buffer[paste_len++] = q[i];
3112 if (p <= data + len - sel_nl_sz &&
3113 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3114 paste_buffer[paste_len++] = '\r';
3120 /* Assume a small paste will be OK in one go. */
3121 if (paste_len < 256) {
3122 luni_send(paste_buffer, paste_len);
3124 sfree(paste_buffer);
3126 paste_pos = paste_hold = paste_len = 0;
3129 get_clip(NULL, NULL);
3132 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3133 int shift, int ctrl)
3136 unsigned long *ldata;
3137 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3141 if (a == MA_DRAG && !raw_mouse)
3146 if (a == MA_DRAG && !raw_mouse)
3159 selpoint.y = y + disptop;
3161 ldata = lineptr(selpoint.y);
3162 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3166 int encstate = 0, r, c;
3168 static int is_down = 0;
3172 encstate = 0x20; /* left button down */
3183 case MBT_WHEEL_DOWN:
3186 default: break; /* placate gcc warning about enum use */
3190 if (xterm_mouse == 1)
3203 default: break; /* placate gcc warning about enum use */
3212 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3213 ldisc_send(abuf, 6);
3217 b = translate_button(b);
3219 if (b == MBT_SELECT && a == MA_CLICK) {
3221 selstate = ABOUT_TO;
3222 selanchor = selpoint;
3224 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3226 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3227 selstate = DRAGGING;
3228 selstart = selanchor = selpoint;
3232 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3233 (b == MBT_EXTEND && a != MA_RELEASE)) {
3234 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3236 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3237 if (posdiff(selpoint, selstart) <
3238 posdiff(selend, selstart) / 2) {
3242 selanchor = selstart;
3244 selstate = DRAGGING;
3246 if (selstate != ABOUT_TO && selstate != DRAGGING)
3247 selanchor = selpoint;
3248 selstate = DRAGGING;
3249 if (poslt(selpoint, selanchor)) {
3250 selstart = selpoint;
3254 selstart = selanchor;
3259 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3260 if (selstate == DRAGGING) {
3262 * We've completed a selection. We now transfer the
3263 * data to the clipboard.
3265 clipme(selstart, selend);
3266 selstate = SELECTED;
3268 selstate = NO_SELECTION;
3269 } else if (b == MBT_PASTE
3270 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3281 sfree(paste_buffer);
3288 static long last_paste = 0;
3289 long now, paste_diff;
3294 /* Don't wait forever to paste */
3296 now = GetTickCount();
3297 paste_diff = now - last_paste;
3298 if (paste_diff >= 0 && paste_diff < 450)
3303 while (paste_pos < paste_len) {
3305 while (n + paste_pos < paste_len) {
3306 if (paste_buffer[paste_pos + n++] == '\r')
3309 luni_send(paste_buffer + paste_pos, n);
3312 if (paste_pos < paste_len) {
3317 sfree(paste_buffer);
3322 static void deselect(void)
3324 selstate = NO_SELECTION;
3325 selstart.x = selstart.y = selend.x = selend.y = 0;
3328 void term_deselect(void)
3334 int term_ldisc(int option)
3336 if (option == LD_ECHO)
3337 return term_echoing;
3338 if (option == LD_EDIT)
3339 return term_editing;
3344 * from_backend(), to get data from the backend for the terminal.
3346 int from_backend(int is_stderr, char *data, int len)
3349 if (inbuf_head >= INBUF_SIZE)
3351 inbuf[inbuf_head++] = *data++;
3355 * We process all stdout/stderr data immediately we receive it,
3356 * and don't return until it's all gone. Therefore, there's no
3357 * reason at all to return anything other than zero from this
3360 * This is a slightly suboptimal way to deal with SSH2 - in
3361 * principle, the window mechanism would allow us to continue
3362 * to accept data on forwarded ports and X connections even
3363 * while the terminal processing was going slowly - but we
3364 * can't do the 100% right thing without moving the terminal
3365 * processing into a separate thread, and that might hurt
3366 * portability. So we manage stdout buffering the old SSH1 way:
3367 * if the terminal processing goes slowly, the whole SSH
3368 * connection stops accepting data until it's ready.
3370 * In practice, I can't imagine this causing serious trouble.
3376 * Log session traffic.
3378 void logtraffic(unsigned char c, int logmode)
3380 if (cfg.logtype > 0) {
3381 if (cfg.logtype == logmode) {
3382 /* deferred open file from pgm start? */
3391 void settimstr(char *ta, int no_sec);
3392 char *subslfcode(char *dest, char *src, char *dstrt);
3393 char *stpncpy(char *dst, const char *src, size_t maxlen);
3395 char currlogfilename[FILENAME_MAX];
3397 /* open log file append/overwrite mode */
3407 sprintf(writemod, "wb"); /* default to rewrite */
3410 tm = *localtime(&t);
3412 /* substitute special codes in file name */
3413 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3415 lgfp = fopen(currlogfilename, "r"); /* file already present? */
3419 i = askappend(currlogfilename);
3421 writemod[0] = 'a'; /* set append mode */
3422 else if (i == 0) { /* cancelled */
3424 cfg.logtype = 0; /* disable logging */
3429 lgfp = fopen(currlogfilename, writemod);
3430 if (lgfp) { /* enter into event log */
3431 sprintf(buf, "%s session log (%s mode) to file : ",
3432 (writemod[0] == 'a') ? "Appending" : "Writing new",
3433 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3434 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3435 /* Make sure we do not exceed the output buffer size */
3436 strncat(buf, currlogfilename, 128);
3437 buf[strlen(buf)] = '\0';
3440 /* --- write header line into log file */
3441 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3442 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
3444 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3448 void logfclose(void)
3457 * translate format codes into time/date strings
3458 * and insert them into log file name
3460 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3462 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3463 char buf[10], *bufp;
3465 char *ds = d; /* save start pos. */
3466 int len = FILENAME_MAX-1;
3469 /* Let (bufp, len) be the string to append. */
3470 bufp = buf; /* don't usually override this */
3474 if (*s) switch (c = *s++, tolower(c)) {
3476 size = strftime(buf, sizeof(buf), "%Y", tm);
3479 size = strftime(buf, sizeof(buf), "%m", tm);
3482 size = strftime(buf, sizeof(buf), "%d", tm);
3485 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3489 size = strlen(bufp);
3503 memcpy(d, bufp, size);