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);
206 * Resize a line to make it `cols' columns wide.
208 unsigned long *resizeline(unsigned long *line, int cols)
211 unsigned long lineattrs;
213 if (line[0] != (unsigned long)cols) {
215 * This line is the wrong length, which probably means it
216 * hasn't been accessed since a resize. Resize it now.
219 lineattrs = line[oldlen + 1];
220 line = srealloc(line, TSIZE * (2 + cols));
222 for (i = oldlen; i < cols; i++)
223 line[i + 1] = ERASE_CHAR;
224 line[cols + 1] = lineattrs & LATTR_MODE;
231 * Retrieve a line of the screen or of the scrollback, according to
232 * whether the y coordinate is non-negative or negative
235 unsigned long *lineptr(int y, int lineno)
237 unsigned long *line, *newline;
245 whichtree = scrollback;
246 treeindex = y + count234(scrollback);
248 line = index234(whichtree, treeindex);
250 /* We assume that we don't screw up and retrieve something out of range. */
251 assert(line != NULL);
253 newline = resizeline(line, cols);
254 if (newline != line) {
255 delpos234(whichtree, treeindex);
256 addpos234(whichtree, newline, treeindex);
263 #define lineptr(x) lineptr(x,__LINE__)
265 * Set up power-on settings for the terminal.
267 static void power_on(void)
269 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
272 alt_b = marg_b = rows - 1;
277 for (i = 0; i < cols; i++)
278 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
280 alt_om = dec_om = cfg.dec_om;
281 alt_wnext = wrapnext = alt_ins = insert = FALSE;
282 alt_wrap = wrap = cfg.wrap_mode;
285 alt_sco_acs = sco_acs = 0;
286 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
291 save_attr = curr_attr = ATTR_DEFAULT;
292 term_editing = term_echoing = FALSE;
293 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
294 app_cursor_keys = cfg.app_cursor;
295 app_keypad_keys = cfg.app_keypad;
297 blink_is_real = cfg.blinktext;
298 erase_char = ERASE_CHAR;
302 for (i = 0; i < 256; i++)
303 wordness[i] = cfg.wordness[i];
307 erase_lots(FALSE, TRUE, TRUE);
309 erase_lots(FALSE, TRUE, TRUE);
314 * Force a screen update.
316 void term_update(void)
323 if ((seen_key_event && (cfg.scroll_on_key)) ||
324 (seen_disp_event && (cfg.scroll_on_disp))) {
325 disptop = 0; /* return to main screen */
326 seen_disp_event = seen_key_event = 0;
329 sys_cursor(curs.x, curs.y - disptop);
335 * Same as power_on(), but an external function.
337 void term_pwron(void)
347 * Clear the scrollback.
349 void term_clrsb(void)
353 while ((line = delpos234(scrollback, 0)) != NULL) {
360 * Initialise the terminal.
364 screen = alt_screen = scrollback = NULL;
366 disptext = dispcurs = NULL;
371 beephead = beeptail = NULL;
374 beep_overloaded = FALSE;
378 * Set up the terminal for a given size.
380 void term_size(int newrows, int newcols, int newsavelines)
383 unsigned long *newdisp, *line;
386 int save_alt_which = alt_which;
388 if (newrows == rows && newcols == cols && newsavelines == savelines)
389 return; /* nothing to do */
395 alt_b = marg_b = newrows - 1;
398 scrollback = newtree234(NULL);
399 screen = newtree234(NULL);
404 * Resize the screen and scrollback. We only need to shift
405 * lines around within our data structures, because lineptr()
406 * will take care of resizing each individual line if
409 * - If the new screen and the old screen differ in length, we
410 * must shunt some lines in from the scrollback or out to
413 * - If doing that fails to provide us with enough material to
414 * fill the new screen (i.e. the number of rows needed in
415 * the new screen exceeds the total number in the previous
416 * screen+scrollback), we must invent some blank lines to
419 * - Then, if the new scrollback length is less than the
420 * amount of scrollback we actually have, we must throw some
423 sblen = count234(scrollback);
424 /* Do this loop to expand the screen if newrows > rows */
425 for (i = rows; i < newrows; i++) {
427 line = delpos234(scrollback, --sblen);
429 line = smalloc(TSIZE * (newcols + 2));
431 for (j = 0; j <= newcols; j++)
432 line[j + 1] = ERASE_CHAR;
434 addpos234(screen, line, 0);
436 /* Do this loop to shrink the screen if newrows < rows */
437 for (i = newrows; i < rows; i++) {
438 line = delpos234(screen, 0);
439 addpos234(scrollback, line, sblen++);
441 assert(count234(screen) == newrows);
442 while (sblen > newsavelines) {
443 line = delpos234(scrollback, 0);
447 assert(count234(scrollback) <= newsavelines);
450 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
451 for (i = 0; i < newrows * (newcols + 1); i++)
452 newdisp[i] = ATTR_INVALID;
457 newalt = newtree234(NULL);
458 for (i = 0; i < newrows; i++) {
459 line = smalloc(TSIZE * (newcols + 2));
461 for (j = 0; j <= newcols; j++)
462 line[j + 1] = erase_char;
463 addpos234(newalt, line, i);
466 while (NULL != (line = delpos234(alt_screen, 0)))
468 freetree234(alt_screen);
472 tabs = srealloc(tabs, newcols * sizeof(*tabs));
475 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
476 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
480 curs.y += newrows - rows;
483 if (curs.y >= newrows)
484 curs.y = newrows - 1;
485 if (curs.x >= newcols)
486 curs.x = newcols - 1;
488 wrapnext = alt_wnext = FALSE;
492 savelines = newsavelines;
495 swap_screen(save_alt_which);
504 static void swap_screen(int which)
509 if (which == alt_which)
536 wrapnext = alt_wnext;
548 sco_acs = alt_sco_acs;
555 * Update the scroll bar.
557 static void update_sbar(void)
561 nscroll = count234(scrollback);
563 set_sbar(nscroll + rows, nscroll + disptop, rows);
567 * Check whether the region bounded by the two pointers intersects
568 * the scroll region, and de-select the on-screen selection if so.
570 static void check_selection(pos from, pos to)
572 if (poslt(from, selend) && poslt(selstart, to))
577 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
578 * for backward.) `sb' is TRUE if the scrolling is permitted to
579 * affect the scrollback buffer.
581 * NB this function invalidates all pointers into lines of the
582 * screen data structures. In particular, you MUST call fix_cpos
583 * after calling scroll() and before doing anything else that
584 * uses the cpos shortcut pointer.
586 static void scroll(int topline, int botline, int lines, int sb)
588 unsigned long *line, *line2;
591 if (topline != 0 || alt_which != 0)
596 line = delpos234(screen, botline);
597 line = resizeline(line, cols);
598 for (i = 0; i < cols; i++)
599 line[i + 1] = erase_char;
601 addpos234(screen, line, topline);
603 if (selstart.y >= topline && selstart.y <= botline) {
605 if (selstart.y > botline) {
606 selstart.y = botline;
610 if (selend.y >= topline && selend.y <= botline) {
612 if (selend.y > botline) {
622 line = delpos234(screen, topline);
623 if (sb && savelines > 0) {
624 int sblen = count234(scrollback);
626 * We must add this line to the scrollback. We'll
627 * remove a line from the top of the scrollback to
628 * replace it, or allocate a new one if the
629 * scrollback isn't full.
631 if (sblen == savelines) {
632 sblen--, line2 = delpos234(scrollback, 0);
634 line2 = smalloc(TSIZE * (cols + 2));
637 addpos234(scrollback, line, sblen);
640 line = resizeline(line, cols);
641 for (i = 0; i < cols; i++)
642 line[i + 1] = erase_char;
644 addpos234(screen, line, botline);
646 if (selstart.y >= topline && selstart.y <= botline) {
648 if (selstart.y < topline) {
649 selstart.y = topline;
653 if (selend.y >= topline && selend.y <= botline) {
655 if (selend.y < topline) {
667 * Move the cursor to a given position, clipping at boundaries. We
668 * may or may not want to clip at the scroll margin: marg_clip is 0
669 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
670 * even _being_ outside the margins.
672 static void move(int x, int y, int marg_clip)
679 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
681 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
695 * Save or restore the cursor and SGR mode.
697 static void save_cursor(int save)
701 save_attr = curr_attr;
704 save_csattr = cset_attr[cset];
705 save_sco_acs = sco_acs;
708 /* Make sure the window hasn't shrunk since the save */
714 curr_attr = save_attr;
717 cset_attr[cset] = save_csattr;
718 sco_acs = save_sco_acs;
721 erase_char = (' ' | ATTR_ASCII |
722 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
727 * Erase a large portion of the screen: the whole screen, or the
728 * whole line, or parts thereof.
730 static void erase_lots(int line_only, int from_begin, int to_end)
734 unsigned long *ldata;
756 check_selection(start, end);
758 /* Clear screen also forces a full window redraw, just in case. */
759 if (start.y == 0 && start.x == 0 && end.y == rows)
762 ldata = lineptr(start.y);
763 while (poslt(start, end)) {
764 if (start.x == cols && !erase_lattr)
765 ldata[start.x] &= ~LATTR_WRAPPED;
767 ldata[start.x] = erase_char;
768 if (incpos(start) && start.y < rows)
769 ldata = lineptr(start.y);
774 * Insert or delete characters within the current line. n is +ve if
775 * insertion is desired, and -ve for deletion.
777 static void insch(int n)
779 int dir = (n < 0 ? -1 : +1);
782 unsigned long *ldata;
784 n = (n < 0 ? -n : n);
785 if (n > cols - curs.x)
787 m = cols - curs.x - n;
789 cursplus.x = curs.x + n;
790 check_selection(curs, cursplus);
791 ldata = lineptr(curs.y);
793 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
795 ldata[curs.x + m++] = erase_char;
797 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
799 ldata[curs.x + n] = erase_char;
804 * Toggle terminal mode `mode' to state `state'. (`query' indicates
805 * whether the mode is a DEC private one or a normal one.)
807 static void toggle_mode(int mode, int query, int state)
813 case 1: /* application cursor keys */
814 app_cursor_keys = state;
816 case 2: /* VT52 mode */
819 blink_is_real = FALSE;
822 blink_is_real = cfg.blinktext;
825 case 3: /* 80/132 columns */
827 request_resize(state ? 132 : 80, rows, 1);
830 case 5: /* reverse video */
832 * Toggle reverse video. If we receive an OFF within the
833 * visual bell timeout period after an ON, we trigger an
834 * effective visual bell, so that ESC[?5hESC[?5l will
835 * always be an actually _visible_ visual bell.
837 ticks = GetTickCount();
838 if (rvideo && !state && /* we're turning it off */
839 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
840 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
841 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
842 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
843 } else if (!rvideo && state) {
844 /* This is an ON, so we notice the time and save it. */
845 rvbell_timeout = ticks + VBELL_TIMEOUT;
848 seen_disp_event = TRUE;
852 case 6: /* DEC origin mode */
855 case 7: /* auto wrap */
858 case 8: /* auto key repeat */
861 case 10: /* set local edit mode */
862 term_editing = state;
863 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
865 case 25: /* enable/disable cursor */
866 compatibility2(OTHER, VT220);
868 seen_disp_event = TRUE;
870 case 47: /* alternate screen */
871 compatibility(OTHER);
876 case 1000: /* xterm mouse 1 */
877 xterm_mouse = state ? 1 : 0;
878 set_raw_mouse_mode(state);
880 case 1002: /* xterm mouse 2 */
881 xterm_mouse = state ? 2 : 0;
882 set_raw_mouse_mode(state);
886 case 4: /* set insert mode */
887 compatibility(VT102);
890 case 12: /* set echo mode */
891 term_echoing = !state;
892 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
894 case 20: /* Return sends ... */
895 cr_lf_return = state;
897 case 34: /* Make cursor BIG */
898 compatibility2(OTHER, VT220);
904 * Process an OSC sequence: set window title or icon name.
906 static void do_osc(void)
910 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
912 osc_string[osc_strlen] = '\0';
913 switch (esc_args[0]) {
916 set_icon(osc_string);
917 if (esc_args[0] == 1)
919 /* fall through: parameter 0 means set both */
922 set_title(osc_string);
929 * Remove everything currently in `inbuf' and stick it up on the
930 * in-memory display. There's a big state machine in here to
931 * process escape sequences...
937 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
938 c = inbuf[inbuf_reap];
941 * Optionally log the session traffic to a file. Useful for
942 * debugging and possibly also useful for actual logging.
944 logtraffic((unsigned char) c, LGTYP_DEBUG);
946 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
947 * be able to display 8-bit characters, but I'll let that go 'cause
951 /* First see about all those translations. */
952 if (termstate == TOPLEVEL) {
957 /* UTF-8 must be stateless so we ignore iso2022. */
958 if (unitab_ctrl[c] != 0xFF)
960 else c = ((unsigned char)c) | ATTR_ASCII;
962 } else if ((c & 0xe0) == 0xc0) {
963 utf_size = utf_state = 1;
964 utf_char = (c & 0x1f);
965 } else if ((c & 0xf0) == 0xe0) {
966 utf_size = utf_state = 2;
967 utf_char = (c & 0x0f);
968 } else if ((c & 0xf8) == 0xf0) {
969 utf_size = utf_state = 3;
970 utf_char = (c & 0x07);
971 } else if ((c & 0xfc) == 0xf8) {
972 utf_size = utf_state = 4;
973 utf_char = (c & 0x03);
974 } else if ((c & 0xfe) == 0xfc) {
975 utf_size = utf_state = 5;
976 utf_char = (c & 0x01);
987 if ((c & 0xC0) != 0x80) {
988 inbuf_reap--; /* This causes the faulting character */
989 c = UCSERR; /* to be logged twice - not really a */
990 utf_state = 0; /* serious problem. */
993 utf_char = (utf_char << 6) | (c & 0x3f);
999 /* Is somebody trying to be evil! */
1001 (c < 0x800 && utf_size >= 2) ||
1002 (c < 0x10000 && utf_size >= 3) ||
1003 (c < 0x200000 && utf_size >= 4) ||
1004 (c < 0x4000000 && utf_size >= 5))
1007 /* Unicode line separator and paragraph separator are CR-LF */
1008 if (c == 0x2028 || c == 0x2029)
1011 /* High controls are probably a Baaad idea too. */
1015 /* The UTF-16 surrogates are not nice either. */
1016 /* The standard give the option of decoding these:
1017 * I don't want to! */
1018 if (c >= 0xD800 && c < 0xE000)
1021 /* ISO 10646 characters now limited to UTF-16 range. */
1025 /* This is currently a TagPhobic application.. */
1026 if (c >= 0xE0000 && c <= 0xE007F)
1029 /* U+FEFF is best seen as a null. */
1032 /* But U+FFFE is an error. */
1033 if (c == 0xFFFE || c == 0xFFFF)
1036 /* Oops this is a 16bit implementation */
1041 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1043 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1045 if (sco_acs == 2) c ^= 0x80;
1048 switch (cset_attr[cset]) {
1050 * Linedraw characters are different from 'ESC ( B'
1051 * only for a small range. For ones outside that
1052 * range, make sure we use the same font as well as
1053 * the same encoding.
1056 if (unitab_ctrl[c] != 0xFF)
1059 c = ((unsigned char) c) | ATTR_LINEDRW;
1063 /* If UK-ASCII, make the '#' a LineDraw Pound */
1065 c = '}' | ATTR_LINEDRW;
1068 /*FALLTHROUGH*/ case ATTR_ASCII:
1069 if (unitab_ctrl[c] != 0xFF)
1072 c = ((unsigned char) c) | ATTR_ASCII;
1075 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1081 /* How about C1 controls ? */
1082 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1083 has_compat(VT220)) {
1084 termstate = SEEN_ESC;
1086 c = '@' + (c & 0x1F);
1089 /* Or the GL control. */
1090 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1091 if (curs.x && !wrapnext)
1095 *cpos = (' ' | curr_attr | ATTR_ASCII);
1097 /* Or normal C0 controls. */
1098 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1100 case '\005': /* terminal type query */
1101 /* Strictly speaking this is VT100 but a VT100 defaults to
1102 * no response. Other terminals respond at their option.
1104 * Don't put a CR in the default string as this tends to
1105 * upset some weird software.
1107 * An xterm returns "xterm" (5 characters)
1109 compatibility(ANSIMIN);
1111 char abuf[256], *s, *d;
1113 for (s = cfg.answerback, d = abuf; *s; s++) {
1115 if (*s >= 'a' && *s <= 'z')
1116 *d++ = (*s - ('a' - 1));
1117 else if ((*s >= '@' && *s <= '_') ||
1118 *s == '?' || (*s & 0x80))
1123 } else if (*s == '^') {
1128 lpage_send(CP_ACP, abuf, d - abuf);
1133 struct beeptime *newbeep;
1136 ticks = GetTickCount();
1138 if (!beep_overloaded) {
1139 newbeep = smalloc(sizeof(struct beeptime));
1140 newbeep->ticks = ticks;
1141 newbeep->next = NULL;
1145 beeptail->next = newbeep;
1151 * Throw out any beeps that happened more than
1155 beephead->ticks < ticks - cfg.bellovl_t) {
1156 struct beeptime *tmp = beephead;
1157 beephead = tmp->next;
1164 if (cfg.bellovl && beep_overloaded &&
1165 ticks - lastbeep >= cfg.bellovl_s) {
1167 * If we're currently overloaded and the
1168 * last beep was more than s seconds ago,
1169 * leave overload mode.
1171 beep_overloaded = FALSE;
1172 } else if (cfg.bellovl && !beep_overloaded &&
1173 nbeeps >= cfg.bellovl_n) {
1175 * Now, if we have n or more beeps
1176 * remaining in the queue, go into overload
1179 beep_overloaded = TRUE;
1184 * Perform an actual beep if we're not overloaded.
1186 if (!cfg.bellovl || !beep_overloaded) {
1188 if (cfg.beep == BELL_VISUAL) {
1190 vbell_timeout = ticks + VBELL_TIMEOUT;
1198 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1199 else if (curs.x == 0 && curs.y > 0)
1200 curs.x = cols - 1, curs.y--;
1206 seen_disp_event = TRUE;
1209 compatibility(VT100);
1213 compatibility(VT100);
1218 termstate = VT52_ESC;
1220 compatibility(ANSIMIN);
1221 termstate = SEEN_ESC;
1229 seen_disp_event = TRUE;
1231 logtraffic((unsigned char) c, LGTYP_ASCII);
1234 if (has_compat(SCOANSI)) {
1236 erase_lots(FALSE, FALSE, TRUE);
1239 seen_disp_event = 1;
1243 compatibility(VT100);
1245 if (curs.y == marg_b)
1246 scroll(marg_t, marg_b, 1, TRUE);
1247 else if (curs.y < rows - 1)
1253 seen_disp_event = 1;
1255 logtraffic((unsigned char) c, LGTYP_ASCII);
1259 pos old_curs = curs;
1260 unsigned long *ldata = lineptr(curs.y);
1264 } while (curs.x < cols - 1 && !tabs[curs.x]);
1266 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1267 if (curs.x >= cols / 2)
1268 curs.x = cols / 2 - 1;
1275 check_selection(old_curs, curs);
1277 seen_disp_event = TRUE;
1281 switch (termstate) {
1283 /* Only graphic characters get this far, ctrls are stripped above */
1284 if (wrapnext && wrap) {
1285 cpos[1] |= LATTR_WRAPPED;
1286 if (curs.y == marg_b)
1287 scroll(marg_t, marg_b, 1, TRUE);
1288 else if (curs.y < rows - 1)
1296 if (selstate != NO_SELECTION) {
1297 pos cursplus = curs;
1299 check_selection(curs, cursplus);
1301 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1302 logtraffic((unsigned char) c, LGTYP_ASCII);
1304 extern int wcwidth(wchar_t ucs);
1309 width = wcwidth((wchar_t) c);
1312 if (curs.x + 1 != cols) {
1313 *cpos++ = c | ATTR_WIDE | curr_attr;
1314 *cpos++ = UCSWIDE | curr_attr;
1319 *cpos++ = c | curr_attr;
1326 if (curs.x == cols) {
1330 if (wrap && vt52_mode) {
1331 cpos[1] |= LATTR_WRAPPED;
1332 if (curs.y == marg_b)
1333 scroll(marg_t, marg_b, 1, TRUE);
1334 else if (curs.y < rows - 1)
1341 seen_disp_event = 1;
1346 * This state is virtually identical to SEEN_ESC, with the
1347 * exception that we have an OSC sequence in the pipeline,
1348 * and _if_ we see a backslash, we process it.
1352 termstate = TOPLEVEL;
1355 /* else fall through */
1357 if (c >= ' ' && c <= '/') {
1364 termstate = TOPLEVEL;
1365 switch (ANSI(c, esc_query)) {
1366 case '[': /* enter CSI mode */
1367 termstate = SEEN_CSI;
1369 esc_args[0] = ARG_DEFAULT;
1372 case ']': /* xterm escape sequences */
1373 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1374 compatibility(OTHER);
1375 termstate = SEEN_OSC;
1378 case '7': /* save cursor */
1379 compatibility(VT100);
1382 case '8': /* restore cursor */
1383 compatibility(VT100);
1385 seen_disp_event = TRUE;
1388 compatibility(VT100);
1389 app_keypad_keys = TRUE;
1392 compatibility(VT100);
1393 app_keypad_keys = FALSE;
1395 case 'D': /* exactly equivalent to LF */
1396 compatibility(VT100);
1397 if (curs.y == marg_b)
1398 scroll(marg_t, marg_b, 1, TRUE);
1399 else if (curs.y < rows - 1)
1403 seen_disp_event = TRUE;
1405 case 'E': /* exactly equivalent to CR-LF */
1406 compatibility(VT100);
1408 if (curs.y == marg_b)
1409 scroll(marg_t, marg_b, 1, TRUE);
1410 else if (curs.y < rows - 1)
1414 seen_disp_event = TRUE;
1416 case 'M': /* reverse index - backwards LF */
1417 compatibility(VT100);
1418 if (curs.y == marg_t)
1419 scroll(marg_t, marg_b, -1, TRUE);
1420 else if (curs.y > 0)
1424 seen_disp_event = TRUE;
1426 case 'Z': /* terminal type query */
1427 compatibility(VT100);
1428 ldisc_send(id_string, strlen(id_string));
1430 case 'c': /* restore power-on settings */
1431 compatibility(VT100);
1434 request_resize(80, rows, 1);
1439 seen_disp_event = TRUE;
1441 case 'H': /* set a tab */
1442 compatibility(VT100);
1443 tabs[curs.x] = TRUE;
1446 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1447 compatibility(VT100);
1449 unsigned long *ldata;
1453 for (i = 0; i < rows; i++) {
1455 for (j = 0; j < cols; j++)
1456 ldata[j] = ATTR_DEFAULT | 'E';
1460 seen_disp_event = TRUE;
1461 scrtop.x = scrtop.y = 0;
1464 check_selection(scrtop, scrbot);
1468 case ANSI('3', '#'):
1469 case ANSI('4', '#'):
1470 case ANSI('5', '#'):
1471 case ANSI('6', '#'):
1472 compatibility(VT100);
1474 unsigned long nlattr;
1475 unsigned long *ldata;
1476 switch (ANSI(c, esc_query)) {
1477 case ANSI('3', '#'):
1480 case ANSI('4', '#'):
1483 case ANSI('5', '#'):
1484 nlattr = LATTR_NORM;
1486 default: /* spiritually case ANSI('6', '#'): */
1487 nlattr = LATTR_WIDE;
1490 ldata = lineptr(curs.y);
1491 ldata[cols] &= ~LATTR_MODE;
1492 ldata[cols] |= nlattr;
1496 case ANSI('A', '('):
1497 compatibility(VT100);
1498 cset_attr[0] = ATTR_GBCHR;
1500 case ANSI('B', '('):
1501 compatibility(VT100);
1502 cset_attr[0] = ATTR_ASCII;
1504 case ANSI('0', '('):
1505 compatibility(VT100);
1506 cset_attr[0] = ATTR_LINEDRW;
1508 case ANSI('U', '('):
1509 compatibility(OTHER);
1510 cset_attr[0] = ATTR_SCOACS;
1513 case ANSI('A', ')'):
1514 compatibility(VT100);
1515 cset_attr[1] = ATTR_GBCHR;
1517 case ANSI('B', ')'):
1518 compatibility(VT100);
1519 cset_attr[1] = ATTR_ASCII;
1521 case ANSI('0', ')'):
1522 compatibility(VT100);
1523 cset_attr[1] = ATTR_LINEDRW;
1525 case ANSI('U', ')'):
1526 compatibility(OTHER);
1527 cset_attr[1] = ATTR_SCOACS;
1530 case ANSI('8', '%'): /* Old Linux code */
1531 case ANSI('G', '%'):
1532 compatibility(OTHER);
1535 case ANSI('@', '%'):
1536 compatibility(OTHER);
1542 termstate = TOPLEVEL; /* default */
1544 if (esc_nargs <= ARGS_MAX) {
1545 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1546 esc_args[esc_nargs - 1] = 0;
1547 esc_args[esc_nargs - 1] =
1548 10 * esc_args[esc_nargs - 1] + c - '0';
1550 termstate = SEEN_CSI;
1551 } else if (c == ';') {
1552 if (++esc_nargs <= ARGS_MAX)
1553 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1554 termstate = SEEN_CSI;
1555 } else if (c < '@') {
1562 termstate = SEEN_CSI;
1564 switch (ANSI(c, esc_query)) {
1565 case 'A': /* move up N lines */
1566 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1567 seen_disp_event = TRUE;
1569 case 'e': /* move down N lines */
1570 compatibility(ANSI);
1573 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1574 seen_disp_event = TRUE;
1576 case ANSI('c', '>'): /* report xterm version */
1577 compatibility(OTHER);
1578 /* this reports xterm version 136 so that VIM can
1579 use the drag messages from the mouse reporting */
1580 ldisc_send("\033[>0;136;0c", 11);
1582 case 'a': /* move right N cols */
1583 compatibility(ANSI);
1586 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1587 seen_disp_event = TRUE;
1589 case 'D': /* move left N cols */
1590 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1591 seen_disp_event = TRUE;
1593 case 'E': /* move down N lines and CR */
1594 compatibility(ANSI);
1595 move(0, curs.y + def(esc_args[0], 1), 1);
1596 seen_disp_event = TRUE;
1598 case 'F': /* move up N lines and CR */
1599 compatibility(ANSI);
1600 move(0, curs.y - def(esc_args[0], 1), 1);
1601 seen_disp_event = TRUE;
1604 case '`': /* set horizontal posn */
1605 compatibility(ANSI);
1606 move(def(esc_args[0], 1) - 1, curs.y, 0);
1607 seen_disp_event = TRUE;
1609 case 'd': /* set vertical posn */
1610 compatibility(ANSI);
1612 (dec_om ? marg_t : 0) + def(esc_args[0],
1615 seen_disp_event = TRUE;
1618 case 'f': /* set horz and vert posns at once */
1620 esc_args[1] = ARG_DEFAULT;
1621 move(def(esc_args[1], 1) - 1,
1622 (dec_om ? marg_t : 0) + def(esc_args[0],
1625 seen_disp_event = TRUE;
1627 case 'J': /* erase screen or parts of it */
1629 unsigned int i = def(esc_args[0], 0) + 1;
1632 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1635 seen_disp_event = TRUE;
1637 case 'K': /* erase line or parts of it */
1639 unsigned int i = def(esc_args[0], 0) + 1;
1642 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1644 seen_disp_event = TRUE;
1646 case 'L': /* insert lines */
1647 compatibility(VT102);
1648 if (curs.y <= marg_b)
1649 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1652 seen_disp_event = TRUE;
1654 case 'M': /* delete lines */
1655 compatibility(VT102);
1656 if (curs.y <= marg_b)
1657 scroll(curs.y, marg_b, def(esc_args[0], 1),
1660 seen_disp_event = TRUE;
1662 case '@': /* insert chars */
1663 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1664 compatibility(VT102);
1665 insch(def(esc_args[0], 1));
1666 seen_disp_event = TRUE;
1668 case 'P': /* delete chars */
1669 compatibility(VT102);
1670 insch(-def(esc_args[0], 1));
1671 seen_disp_event = TRUE;
1673 case 'c': /* terminal type query */
1674 compatibility(VT100);
1675 /* This is the response for a VT102 */
1676 ldisc_send(id_string, strlen(id_string));
1678 case 'n': /* cursor position query */
1679 if (esc_args[0] == 6) {
1681 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1683 ldisc_send(buf, strlen(buf));
1684 } else if (esc_args[0] == 5) {
1685 ldisc_send("\033[0n", 4);
1688 case 'h': /* toggle modes to high */
1690 compatibility(VT100);
1693 for (i = 0; i < esc_nargs; i++)
1694 toggle_mode(esc_args[i], esc_query, TRUE);
1697 case 'l': /* toggle modes to low */
1699 compatibility(VT100);
1702 for (i = 0; i < esc_nargs; i++)
1703 toggle_mode(esc_args[i], esc_query, FALSE);
1706 case 'g': /* clear tabs */
1707 compatibility(VT100);
1708 if (esc_nargs == 1) {
1709 if (esc_args[0] == 0) {
1710 tabs[curs.x] = FALSE;
1711 } else if (esc_args[0] == 3) {
1713 for (i = 0; i < cols; i++)
1718 case 'r': /* set scroll margins */
1719 compatibility(VT100);
1720 if (esc_nargs <= 2) {
1722 top = def(esc_args[0], 1) - 1;
1723 bot = (esc_nargs <= 1
1725 0 ? rows : def(esc_args[1], rows)) - 1;
1728 /* VTTEST Bug 9 - if region is less than 2 lines
1729 * don't change region.
1731 if (bot - top > 0) {
1736 * I used to think the cursor should be
1737 * placed at the top of the newly marginned
1738 * area. Apparently not: VMS TPU falls over
1741 * Well actually it should for Origin mode - RDB
1743 curs.y = (dec_om ? marg_t : 0);
1745 seen_disp_event = TRUE;
1749 case 'm': /* set graphics rendition */
1752 * A VT100 without the AVO only had one attribute, either
1753 * underline or reverse video depending on the cursor type,
1754 * this was selected by CSI 7m.
1757 * This is sometimes DIM, eg on the GIGI and Linux
1759 * This is sometimes INVIS various ANSI.
1761 * This like 22 disables BOLD, DIM and INVIS
1763 * The ANSI colours appear on any terminal that has colour
1764 * (obviously) but the interaction between sgr0 and the
1765 * colours varies but is usually related to the background
1766 * colour erase item.
1767 * The interaction between colour attributes and the mono
1768 * ones is also very implementation dependent.
1770 * The 39 and 49 attributes are likely to be unimplemented.
1773 for (i = 0; i < esc_nargs; i++) {
1774 switch (def(esc_args[i], 0)) {
1775 case 0: /* restore defaults */
1776 curr_attr = ATTR_DEFAULT;
1778 case 1: /* enable bold */
1779 compatibility(VT100AVO);
1780 curr_attr |= ATTR_BOLD;
1782 case 21: /* (enable double underline) */
1783 compatibility(OTHER);
1784 case 4: /* enable underline */
1785 compatibility(VT100AVO);
1786 curr_attr |= ATTR_UNDER;
1788 case 5: /* enable blink */
1789 compatibility(VT100AVO);
1790 curr_attr |= ATTR_BLINK;
1792 case 7: /* enable reverse video */
1793 curr_attr |= ATTR_REVERSE;
1795 case 10: /* SCO acs off */
1796 compatibility(SCOANSI);
1798 case 11: /* SCO acs on */
1799 compatibility(SCOANSI);
1801 case 12: /* SCO acs on flipped */
1802 compatibility(SCOANSI);
1804 case 22: /* disable bold */
1805 compatibility2(OTHER, VT220);
1806 curr_attr &= ~ATTR_BOLD;
1808 case 24: /* disable underline */
1809 compatibility2(OTHER, VT220);
1810 curr_attr &= ~ATTR_UNDER;
1812 case 25: /* disable blink */
1813 compatibility2(OTHER, VT220);
1814 curr_attr &= ~ATTR_BLINK;
1816 case 27: /* disable reverse video */
1817 compatibility2(OTHER, VT220);
1818 curr_attr &= ~ATTR_REVERSE;
1829 curr_attr &= ~ATTR_FGMASK;
1831 (esc_args[i] - 30) << ATTR_FGSHIFT;
1833 case 39: /* default-foreground */
1834 curr_attr &= ~ATTR_FGMASK;
1835 curr_attr |= ATTR_DEFFG;
1846 curr_attr &= ~ATTR_BGMASK;
1848 (esc_args[i] - 40) << ATTR_BGSHIFT;
1850 case 49: /* default-background */
1851 curr_attr &= ~ATTR_BGMASK;
1852 curr_attr |= ATTR_DEFBG;
1857 erase_char = (' ' | ATTR_ASCII |
1859 (ATTR_FGMASK | ATTR_BGMASK)));
1862 case 's': /* save cursor */
1865 case 'u': /* restore cursor */
1867 seen_disp_event = TRUE;
1869 case 't': /* set page size - ie window height */
1871 * VT340/VT420 sequence DECSLPP, DEC only allows values
1872 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1873 * illegal values (eg first arg 1..9) for window changing
1876 compatibility(VT340TEXT);
1878 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1879 request_resize(cols, def(esc_args[0], 24), 0);
1884 compatibility(SCOANSI);
1885 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1888 seen_disp_event = TRUE;
1891 compatibility(SCOANSI);
1892 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1895 seen_disp_event = TRUE;
1897 case ANSI('|', '*'):
1898 /* VT420 sequence DECSNLS
1899 * Set number of lines on screen
1900 * VT420 uses VGA like hardware and can support any size in
1901 * reasonable range (24..49 AIUI) with no default specified.
1903 compatibility(VT420);
1904 if (esc_nargs == 1 && esc_args[0] > 0) {
1905 request_resize(cols,
1906 def(esc_args[0], cfg.height),
1911 case ANSI('|', '$'):
1912 /* VT340/VT420 sequence DECSCPP
1913 * Set number of columns per page
1914 * Docs imply range is only 80 or 132, but I'll allow any.
1916 compatibility(VT340TEXT);
1917 if (esc_nargs <= 1) {
1918 request_resize(def(esc_args[0], cfg.width),
1923 case 'X': /* write N spaces w/o moving cursor */
1924 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1925 compatibility(ANSIMIN);
1927 int n = def(esc_args[0], 1);
1929 unsigned long *p = cpos;
1930 if (n > cols - curs.x)
1934 check_selection(curs, cursplus);
1937 seen_disp_event = TRUE;
1940 case 'x': /* report terminal characteristics */
1941 compatibility(VT100);
1944 int i = def(esc_args[0], 0);
1945 if (i == 0 || i == 1) {
1946 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1948 ldisc_send(buf, 20);
1952 case 'Z': /* BackTab for xterm */
1953 compatibility(OTHER);
1955 int i = def(esc_args[0], 1);
1956 pos old_curs = curs;
1958 for(;i>0 && curs.x>0; i--) {
1961 } while (curs.x >0 && !tabs[curs.x]);
1964 check_selection(old_curs, curs);
1967 case ANSI('L', '='):
1968 compatibility(OTHER);
1969 use_bce = (esc_args[0] <= 0);
1970 erase_char = ERASE_CHAR;
1972 erase_char = (' ' | ATTR_ASCII |
1974 (ATTR_FGMASK | ATTR_BGMASK)));
1976 case ANSI('E', '='):
1977 compatibility(OTHER);
1978 blink_is_real = (esc_args[0] >= 1);
1980 case ANSI('p', '"'):
1981 /* Allow the host to make this emulator a 'perfect' VT102.
1982 * This first appeared in the VT220, but we do need to get
1983 * back to PuTTY mode so I won't check it.
1985 * The arg in 40..42,50 are a PuTTY extension.
1986 * The 2nd arg, 8bit vs 7bit is not checked.
1988 * Setting VT102 mode should also change the Fkeys to
1989 * generate PF* codes as a real VT102 has no Fkeys.
1990 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1993 * Note ESC c will NOT change this!
1996 switch (esc_args[0]) {
1998 compatibility_level &= ~TM_VTXXX;
1999 compatibility_level |= TM_VT102;
2002 compatibility_level &= ~TM_VTXXX;
2003 compatibility_level |= TM_VT220;
2007 if (esc_args[0] > 60 && esc_args[0] < 70)
2008 compatibility_level |= TM_VTXXX;
2012 compatibility_level &= TM_VTXXX;
2015 compatibility_level = TM_PUTTY;
2018 compatibility_level = TM_SCOANSI;
2022 compatibility_level = TM_PUTTY;
2028 /* Change the response to CSI c */
2029 if (esc_args[0] == 50) {
2032 strcpy(id_string, "\033[?");
2033 for (i = 1; i < esc_nargs; i++) {
2035 strcat(id_string, ";");
2036 sprintf(lbuf, "%d", esc_args[i]);
2037 strcat(id_string, lbuf);
2039 strcat(id_string, "c");
2042 /* Is this a good idea ?
2043 * Well we should do a soft reset at this point ...
2045 if (!has_compat(VT420) && has_compat(VT100)) {
2047 request_resize(132, 24, 1);
2049 request_resize(80, 24, 1);
2058 case 'P': /* Linux palette sequence */
2059 termstate = SEEN_OSC_P;
2062 case 'R': /* Linux palette reset */
2065 termstate = TOPLEVEL;
2067 case 'W': /* word-set */
2068 termstate = SEEN_OSC_W;
2081 esc_args[0] = 10 * esc_args[0] + c - '0';
2085 * Grotty hack to support xterm and DECterm title
2086 * sequences concurrently.
2088 if (esc_args[0] == 2) {
2092 /* else fall through */
2094 termstate = OSC_STRING;
2100 * This OSC stuff is EVIL. It takes just one character to get into
2101 * sysline mode and it's not initially obvious how to get out.
2102 * So I've added CR and LF as string aborts.
2103 * This shouldn't effect compatibility as I believe embedded
2104 * control characters are supposed to be interpreted (maybe?)
2105 * and they don't display anything useful anyway.
2109 if (c == '\n' || c == '\r') {
2110 termstate = TOPLEVEL;
2111 } else if (c == 0234 || c == '\007') {
2113 * These characters terminate the string; ST and BEL
2114 * terminate the sequence and trigger instant
2115 * processing of it, whereas ESC goes back to SEEN_ESC
2116 * mode unless it is followed by \, in which case it is
2117 * synonymous with ST in the first place.
2120 termstate = TOPLEVEL;
2121 } else if (c == '\033')
2122 termstate = OSC_MAYBE_ST;
2123 else if (osc_strlen < OSC_STR_MAX)
2124 osc_string[osc_strlen++] = c;
2128 int max = (osc_strlen == 0 ? 21 : 16);
2130 if (c >= '0' && c <= '9')
2132 else if (c >= 'A' && c <= 'A' + max - 10)
2134 else if (c >= 'a' && c <= 'a' + max - 10)
2137 termstate = TOPLEVEL;
2140 osc_string[osc_strlen++] = val;
2141 if (osc_strlen >= 7) {
2142 palette_set(osc_string[0],
2143 osc_string[1] * 16 + osc_string[2],
2144 osc_string[3] * 16 + osc_string[4],
2145 osc_string[5] * 16 + osc_string[6]);
2147 termstate = TOPLEVEL;
2163 esc_args[0] = 10 * esc_args[0] + c - '0';
2166 termstate = OSC_STRING;
2171 termstate = TOPLEVEL;
2172 seen_disp_event = TRUE;
2175 move(curs.x, curs.y - 1, 1);
2178 move(curs.x, curs.y + 1, 1);
2181 move(curs.x + 1, curs.y, 1);
2184 move(curs.x - 1, curs.y, 1);
2187 * From the VT100 Manual
2188 * NOTE: The special graphics characters in the VT100
2189 * are different from those in the VT52
2191 * From VT102 manual:
2192 * 137 _ Blank - Same
2193 * 140 ` Reserved - Humm.
2194 * 141 a Solid rectangle - Similar
2195 * 142 b 1/ - Top half of fraction for the
2196 * 143 c 3/ - subscript numbers below.
2199 * 146 f Degrees - Same
2200 * 147 g Plus or minus - Same
2202 * 151 i Ellipsis (dots)
2205 * 154 l Bar at scan 0
2206 * 155 m Bar at scan 1
2207 * 156 n Bar at scan 2
2208 * 157 o Bar at scan 3 - Similar
2209 * 160 p Bar at scan 4 - Similar
2210 * 161 q Bar at scan 5 - Similar
2211 * 162 r Bar at scan 6 - Same
2212 * 163 s Bar at scan 7 - Similar
2227 cset_attr[cset = 0] = ATTR_LINEDRW;
2230 cset_attr[cset = 0] = ATTR_ASCII;
2237 scroll(0, rows - 1, -1, TRUE);
2238 else if (curs.y > 0)
2244 erase_lots(FALSE, FALSE, TRUE);
2248 erase_lots(TRUE, FALSE, TRUE);
2252 /* XXX Print cursor line */
2255 /* XXX Start controller mode */
2258 /* XXX Stop controller mode */
2262 termstate = VT52_Y1;
2265 ldisc_send("\033/Z", 3);
2268 app_keypad_keys = TRUE;
2271 app_keypad_keys = FALSE;
2274 /* XXX This should switch to VT100 mode not current or default
2275 * VT mode. But this will only have effect in a VT220+
2279 blink_is_real = cfg.blinktext;
2283 /* XXX Enter auto print mode */
2286 /* XXX Exit auto print mode */
2289 /* XXX Print screen */
2295 /* compatibility(ATARI) */
2297 erase_lots(FALSE, FALSE, TRUE);
2301 /* compatibility(ATARI) */
2302 if (curs.y <= marg_b)
2303 scroll(curs.y, marg_b, -1, FALSE);
2306 /* compatibility(ATARI) */
2307 if (curs.y <= marg_b)
2308 scroll(curs.y, marg_b, 1, TRUE);
2311 /* compatibility(ATARI) */
2312 termstate = VT52_FG;
2315 /* compatibility(ATARI) */
2316 termstate = VT52_BG;
2319 /* compatibility(ATARI) */
2320 erase_lots(FALSE, TRUE, FALSE);
2324 /* compatibility(ATARI) */
2328 /* compatibility(ATARI) */
2331 /* case 'j': Save cursor position - broken on ST */
2332 /* case 'k': Restore cursor position */
2334 /* compatibility(ATARI) */
2335 erase_lots(TRUE, TRUE, TRUE);
2341 /* compatibility(ATARI) */
2342 erase_lots(TRUE, TRUE, FALSE);
2345 /* compatibility(ATARI) */
2346 curr_attr |= ATTR_REVERSE;
2349 /* compatibility(ATARI) */
2350 curr_attr &= ~ATTR_REVERSE;
2352 case 'v': /* wrap Autowrap on - Wyse style */
2353 /* compatibility(ATARI) */
2356 case 'w': /* Autowrap off */
2357 /* compatibility(ATARI) */
2362 /* compatibility(OTHER) */
2364 curr_attr = ATTR_DEFAULT;
2366 erase_char = (' ' | ATTR_ASCII |
2368 (ATTR_FGMASK | ATTR_BGMASK)));
2371 /* compatibility(VI50) */
2372 curr_attr |= ATTR_UNDER;
2375 /* compatibility(VI50) */
2376 curr_attr &= ~ATTR_UNDER;
2379 /* compatibility(VI50) */
2381 curr_attr |= ATTR_BOLD;
2384 /* compatibility(VI50) */
2386 curr_attr &= ~ATTR_BOLD;
2392 termstate = VT52_Y2;
2393 move(curs.x, c - ' ', 0);
2396 termstate = TOPLEVEL;
2397 move(c - ' ', curs.y, 0);
2402 termstate = TOPLEVEL;
2403 curr_attr &= ~ATTR_FGMASK;
2404 curr_attr &= ~ATTR_BOLD;
2405 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2406 if ((c & 0x8) || vt52_bold)
2407 curr_attr |= ATTR_BOLD;
2410 erase_char = (' ' | ATTR_ASCII |
2411 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2414 termstate = TOPLEVEL;
2415 curr_attr &= ~ATTR_BGMASK;
2416 curr_attr &= ~ATTR_BLINK;
2417 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2419 /* Note: bold background */
2421 curr_attr |= ATTR_BLINK;
2424 erase_char = (' ' | ATTR_ASCII |
2425 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2428 default: break; /* placate gcc warning about enum use */
2430 if (selstate != NO_SELECTION) {
2431 pos cursplus = curs;
2433 check_selection(curs, cursplus);
2441 * Compare two lines to determine whether they are sufficiently
2442 * alike to scroll-optimise one to the other. Return the degree of
2445 static int linecmp(unsigned long *a, unsigned long *b)
2449 for (i = n = 0; i < cols; i++)
2450 n += (*a++ == *b++);
2456 * Given a context, update the window. Out of paranoia, we don't
2457 * allow WM_PAINT responses to do scrolling optimisations.
2459 static void do_paint(Context ctx, int may_optimise)
2461 int i, j, our_curs_y;
2462 unsigned long rv, cursor;
2465 long cursor_background = ERASE_CHAR;
2469 * Check the visual bell state.
2472 ticks = GetTickCount();
2473 if (ticks - vbell_timeout >= 0)
2477 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2480 * screen array, disptop, scrtop,
2482 * cfg.blinkpc, blink_is_real, tblinker,
2483 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2486 /* Has the cursor position or type changed ? */
2489 if (blinker || !cfg.blink_cur)
2490 cursor = TATTR_ACTCURS;
2494 cursor = TATTR_PASCURS;
2496 cursor |= TATTR_RIGHTCURS;
2499 our_curs_y = curs.y - disptop;
2501 if (dispcurs && (curstype != cursor ||
2503 disptext + our_curs_y * (cols + 1) + curs.x)) {
2504 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2505 dispcurs[-1] |= ATTR_INVALID;
2506 if ((*dispcurs & ATTR_WIDE))
2507 dispcurs[1] |= ATTR_INVALID;
2508 *dispcurs |= ATTR_INVALID;
2513 /* The normal screen data */
2514 for (i = 0; i < rows; i++) {
2515 unsigned long *ldata;
2517 int idx, dirty_line, dirty_run;
2518 unsigned long attr = 0;
2519 int updated_line = 0;
2522 int last_run_dirty = 0;
2524 scrpos.y = i + disptop;
2525 ldata = lineptr(scrpos.y);
2526 lattr = (ldata[cols] & LATTR_MODE);
2528 idx = i * (cols + 1);
2529 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2530 disptext[idx + cols] = ldata[cols];
2532 for (j = 0; j < cols; j++, idx++) {
2533 unsigned long tattr, tchar;
2534 unsigned long *d = ldata + j;
2538 tchar = (*d & (CHAR_MASK | CSET_MASK));
2539 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2540 switch (tchar & CSET_MASK) {
2542 tchar = unitab_line[tchar & 0xFF];
2545 tchar = unitab_xterm[tchar & 0xFF];
2548 tchar = unitab_scoacs[tchar&0xFF];
2551 tattr |= (tchar & CSET_MASK);
2554 /* Video reversing things */
2556 ^ (posle(selstart, scrpos) &&
2557 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2559 /* 'Real' blinking ? */
2560 if (blink_is_real && (tattr & ATTR_BLINK)) {
2561 if (has_focus && tblinker) {
2563 tattr &= ~CSET_MASK;
2566 tattr &= ~ATTR_BLINK;
2569 /* Cursor here ? Save the 'background' */
2570 if (i == our_curs_y && j == curs.x) {
2571 cursor_background = tattr | tchar;
2572 dispcurs = disptext + idx;
2575 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2578 break_run = (tattr != attr || j - start >= sizeof(ch));
2580 /* Special hack for VT100 Linedraw glyphs */
2581 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2582 && tchar <= 0xBD) break_run = TRUE;
2584 if (!dbcs_screenfont && !dirty_line) {
2585 if ((tchar | tattr) == disptext[idx])
2587 else if (!dirty_run && ccount == 1)
2592 if ((dirty_run || last_run_dirty) && ccount > 0) {
2593 do_text(ctx, start, i, ch, ccount, attr, lattr);
2599 if (dbcs_screenfont)
2600 last_run_dirty = dirty_run;
2601 dirty_run = dirty_line;
2604 if ((tchar | tattr) != disptext[idx])
2606 ch[ccount++] = (char) tchar;
2607 disptext[idx] = tchar | tattr;
2609 /* If it's a wide char step along to the next one. */
2610 if (tattr & ATTR_WIDE) {
2614 /* Cursor is here ? Ouch! */
2615 if (i == our_curs_y && j == curs.x) {
2616 cursor_background = *d;
2617 dispcurs = disptext + idx;
2619 if (disptext[idx] != *d)
2625 if (dirty_run && ccount > 0) {
2626 do_text(ctx, start, i, ch, ccount, attr, lattr);
2630 /* Cursor on this line ? (and changed) */
2631 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2632 ch[0] = (char) (cursor_background & CHAR_MASK);
2633 attr = (cursor_background & ATTR_MASK) | cursor;
2634 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2641 * Flick the switch that says if blinking things should be shown or hidden.
2644 void term_blink(int flg)
2646 static long last_blink = 0;
2647 static long last_tblink = 0;
2648 long now, blink_diff;
2650 now = GetTickCount();
2651 blink_diff = now - last_tblink;
2653 /* Make sure the text blinks no more than 2Hz */
2654 if (blink_diff < 0 || blink_diff > 450) {
2656 tblinker = !tblinker;
2665 blink_diff = now - last_blink;
2667 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2668 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2676 * Invalidate the whole screen so it will be repainted in full.
2678 void term_invalidate(void)
2682 for (i = 0; i < rows * (cols + 1); i++)
2683 disptext[i] = ATTR_INVALID;
2687 * Paint the window in response to a WM_PAINT message.
2689 void term_paint(Context ctx, int l, int t, int r, int b)
2691 int i, j, left, top, right, bottom;
2693 left = l / font_width;
2694 right = (r - 1) / font_width;
2695 top = t / font_height;
2696 bottom = (b - 1) / font_height;
2697 for (i = top; i <= bottom && i < rows; i++) {
2698 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2699 for (j = left; j <= right && j < cols; j++)
2700 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2702 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2703 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2706 /* This should happen soon enough, also for some reason it sometimes
2707 * fails to actually do anything when re-sizing ... painting the wrong
2709 do_paint (ctx, FALSE);
2714 * Attempt to scroll the scrollback. The second parameter gives the
2715 * position we want to scroll to; the first is +1 to denote that
2716 * this position is relative to the beginning of the scrollback, -1
2717 * to denote it is relative to the end, and 0 to denote that it is
2718 * relative to the current position.
2720 void term_scroll(int rel, int where)
2722 int sbtop = -count234(scrollback);
2724 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2725 if (disptop < sbtop)
2733 static void clipme(pos top, pos bottom)
2736 wchar_t *wbptr; /* where next char goes within workbuf */
2737 int wblen = 0; /* workbuf len */
2738 int buflen; /* amount of memory allocated to workbuf */
2740 buflen = 5120; /* Default size */
2741 workbuf = smalloc(buflen * sizeof(wchar_t));
2742 wbptr = workbuf; /* start filling here */
2744 while (poslt(top, bottom)) {
2746 unsigned long *ldata = lineptr(top.y);
2752 if (!(ldata[cols] & LATTR_WRAPPED)) {
2753 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2754 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2755 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2756 && poslt(top, nlpos))
2758 if (poslt(nlpos, bottom))
2761 while (poslt(top, bottom) && poslt(top, nlpos)) {
2764 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2766 wchar_t cbuf[16], *p;
2767 int uc = (ldata[top.x] & 0xFFFF);
2770 if (uc == UCSWIDE) {
2775 switch (uc & CSET_MASK) {
2778 uc = unitab_xterm[uc & 0xFF];
2782 uc = unitab_line[uc & 0xFF];
2785 uc = unitab_scoacs[uc&0xFF];
2788 switch (uc & CSET_MASK) {
2790 uc = unitab_font[uc & 0xFF];
2793 uc = unitab_oemcp[uc & 0xFF];
2797 set = (uc & CSET_MASK);
2798 c = (uc & CHAR_MASK);
2802 if (DIRECT_FONT(uc)) {
2803 if (c >= ' ' && c != 0x7F) {
2804 unsigned char buf[4];
2807 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2809 buf[1] = (unsigned char) ldata[top.x + 1];
2810 rv = MultiByteToWideChar(font_codepage,
2811 0, buf, 2, wbuf, 4);
2815 rv = MultiByteToWideChar(font_codepage,
2816 0, buf, 1, wbuf, 4);
2820 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2827 for (p = cbuf; *p; p++) {
2828 /* Enough overhead for trailing NL and nul */
2829 if (wblen >= buflen - 16) {
2832 sizeof(wchar_t) * (buflen += 100));
2833 wbptr = workbuf + wblen;
2842 for (i = 0; i < sel_nl_sz; i++) {
2844 *wbptr++ = sel_nl[i];
2852 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2853 if (buflen > 0) /* indicates we allocated this buffer */
2857 void term_copyall(void)
2860 top.y = -count234(scrollback);
2866 * The wordness array is mainly for deciding the disposition of the US-ASCII
2869 static int wordtype(int uc)
2872 int start, end, ctype;
2873 } *wptr, ucs_words[] = {
2879 0x037e, 0x037e, 1}, /* Greek question mark */
2881 0x0387, 0x0387, 1}, /* Greek ano teleia */
2883 0x055a, 0x055f, 1}, /* Armenian punctuation */
2885 0x0589, 0x0589, 1}, /* Armenian full stop */
2887 0x0700, 0x070d, 1}, /* Syriac punctuation */
2889 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2891 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2893 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2895 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2897 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2899 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2901 0x2000, 0x200a, 0}, /* Various spaces */
2903 0x2070, 0x207f, 2}, /* superscript */
2905 0x2080, 0x208f, 2}, /* subscript */
2907 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2909 0x3000, 0x3000, 0}, /* ideographic space */
2911 0x3001, 0x3020, 1}, /* ideographic punctuation */
2913 0x303f, 0x309f, 3}, /* Hiragana */
2915 0x30a0, 0x30ff, 3}, /* Katakana */
2917 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2919 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2921 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2923 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2925 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2927 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2929 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2931 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2933 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2938 uc &= (CSET_MASK | CHAR_MASK);
2940 switch (uc & CSET_MASK) {
2942 uc = unitab_xterm[uc & 0xFF];
2945 uc = unitab_line[uc & 0xFF];
2948 uc = unitab_scoacs[uc&0xFF];
2951 switch (uc & CSET_MASK) {
2953 uc = unitab_font[uc & 0xFF];
2956 uc = unitab_oemcp[uc & 0xFF];
2961 return wordness[uc];
2963 for (wptr = ucs_words; wptr->start; wptr++) {
2964 if (uc >= wptr->start && uc <= wptr->end)
2972 * Spread the selection outwards according to the selection mode.
2974 static pos sel_spread_half(pos p, int dir)
2976 unsigned long *ldata;
2979 ldata = lineptr(p.y);
2984 * In this mode, every character is a separate unit, except
2985 * for runs of spaces at the end of a non-wrapping line.
2987 if (!(ldata[cols] & LATTR_WRAPPED)) {
2988 unsigned long *q = ldata + cols;
2989 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2991 if (q == ldata + cols)
2993 if (p.x >= q - ldata)
2994 p.x = (dir == -1 ? q - ldata : cols - 1);
2999 * In this mode, the units are maximal runs of characters
3000 * whose `wordness' has the same value.
3002 wvalue = wordtype(ldata[p.x]);
3004 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3007 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3013 * In this mode, every line is a unit.
3015 p.x = (dir == -1 ? 0 : cols - 1);
3021 static void sel_spread(void)
3023 selstart = sel_spread_half(selstart, -1);
3025 selend = sel_spread_half(selend, +1);
3029 void term_do_paste(void)
3034 get_clip(&data, &len);
3039 sfree(paste_buffer);
3040 paste_pos = paste_hold = paste_len = 0;
3041 paste_buffer = smalloc(len * sizeof(wchar_t));
3044 while (p < data + len) {
3045 while (p < data + len &&
3046 !(p <= data + len - sel_nl_sz &&
3047 !memcmp(p, sel_nl, sizeof(sel_nl))))
3052 for (i = 0; i < p - q; i++) {
3053 paste_buffer[paste_len++] = q[i];
3057 if (p <= data + len - sel_nl_sz &&
3058 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3059 paste_buffer[paste_len++] = '\r';
3065 /* Assume a small paste will be OK in one go. */
3066 if (paste_len < 256) {
3067 luni_send(paste_buffer, paste_len);
3069 sfree(paste_buffer);
3071 paste_pos = paste_hold = paste_len = 0;
3074 get_clip(NULL, NULL);
3077 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3078 int shift, int ctrl)
3081 unsigned long *ldata;
3097 selpoint.y = y + disptop;
3099 ldata = lineptr(selpoint.y);
3100 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3104 int encstate = 0, r, c;
3106 static int is_down = 0;
3110 encstate = 0x20; /* left button down */
3121 case MBT_WHEEL_DOWN:
3124 default: break; /* placate gcc warning about enum use */
3128 if (xterm_mouse == 1)
3141 default: break; /* placate gcc warning about enum use */
3150 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3151 ldisc_send(abuf, 6);
3155 b = translate_button(b);
3157 if (b == MBT_SELECT && a == MA_CLICK) {
3159 selstate = ABOUT_TO;
3160 selanchor = selpoint;
3162 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3164 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3165 selstate = DRAGGING;
3166 selstart = selanchor = selpoint;
3170 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3171 (b == MBT_EXTEND && a != MA_RELEASE)) {
3172 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3174 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3175 if (posdiff(selpoint, selstart) <
3176 posdiff(selend, selstart) / 2) {
3180 selanchor = selstart;
3182 selstate = DRAGGING;
3184 if (selstate != ABOUT_TO && selstate != DRAGGING)
3185 selanchor = selpoint;
3186 selstate = DRAGGING;
3187 if (poslt(selpoint, selanchor)) {
3188 selstart = selpoint;
3192 selstart = selanchor;
3197 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3198 if (selstate == DRAGGING) {
3200 * We've completed a selection. We now transfer the
3201 * data to the clipboard.
3203 clipme(selstart, selend);
3204 selstate = SELECTED;
3206 selstate = NO_SELECTION;
3207 } else if (b == MBT_PASTE
3208 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3219 sfree(paste_buffer);
3226 static long last_paste = 0;
3227 long now, paste_diff;
3232 /* Don't wait forever to paste */
3234 now = GetTickCount();
3235 paste_diff = now - last_paste;
3236 if (paste_diff >= 0 && paste_diff < 450)
3241 while (paste_pos < paste_len) {
3243 while (n + paste_pos < paste_len) {
3244 if (paste_buffer[paste_pos + n++] == '\r')
3247 luni_send(paste_buffer + paste_pos, n);
3250 if (paste_pos < paste_len) {
3255 sfree(paste_buffer);
3260 static void deselect(void)
3262 selstate = NO_SELECTION;
3263 selstart.x = selstart.y = selend.x = selend.y = 0;
3266 void term_deselect(void)
3272 int term_ldisc(int option)
3274 if (option == LD_ECHO)
3275 return term_echoing;
3276 if (option == LD_EDIT)
3277 return term_editing;
3282 * from_backend(), to get data from the backend for the terminal.
3284 int from_backend(int is_stderr, char *data, int len)
3287 if (inbuf_head >= INBUF_SIZE)
3289 inbuf[inbuf_head++] = *data++;
3293 * We process all stdout/stderr data immediately we receive it,
3294 * and don't return until it's all gone. Therefore, there's no
3295 * reason at all to return anything other than zero from this
3298 * This is a slightly suboptimal way to deal with SSH2 - in
3299 * principle, the window mechanism would allow us to continue
3300 * to accept data on forwarded ports and X connections even
3301 * while the terminal processing was going slowly - but we
3302 * can't do the 100% right thing without moving the terminal
3303 * processing into a separate thread, and that might hurt
3304 * portability. So we manage stdout buffering the old SSH1 way:
3305 * if the terminal processing goes slowly, the whole SSH
3306 * connection stops accepting data until it's ready.
3308 * In practice, I can't imagine this causing serious trouble.
3314 * Log session traffic.
3316 void logtraffic(unsigned char c, int logmode)
3318 if (cfg.logtype > 0) {
3319 if (cfg.logtype == logmode) {
3320 /* deferred open file from pgm start? */
3329 /* open log file append/overwrite mode */
3339 sprintf(writemod, "wb"); /* default to rewrite */
3340 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3344 i = askappend(cfg.logfilename);
3346 writemod[0] = 'a'; /* set append mode */
3347 else if (i == 0) { /* cancelled */
3349 cfg.logtype = 0; /* disable logging */
3354 lgfp = fopen(cfg.logfilename, writemod);
3355 if (lgfp) { /* enter into event log */
3356 sprintf(buf, "%s session log (%s mode) to file : ",
3357 (writemod[0] == 'a') ? "Appending" : "Writing new",
3358 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3359 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3360 /* Make sure we do not exceed the output buffer size */
3361 strncat(buf, cfg.logfilename, 128);
3362 buf[strlen(buf)] = '\0';
3365 /* --- write header line iinto log file */
3366 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3369 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3371 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3375 void logfclose(void)