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...
938 * Optionally log the session traffic to a file. Useful for
939 * debugging and possibly also useful for actual logging.
941 if (cfg.logtype == LGTYP_DEBUG)
942 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
943 logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
946 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
947 c = inbuf[inbuf_reap];
949 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
950 * be able to display 8-bit characters, but I'll let that go 'cause
954 /* First see about all those translations. */
955 if (termstate == TOPLEVEL) {
960 /* UTF-8 must be stateless so we ignore iso2022. */
961 if (unitab_ctrl[c] != 0xFF)
963 else c = ((unsigned char)c) | ATTR_ASCII;
965 } else if ((c & 0xe0) == 0xc0) {
966 utf_size = utf_state = 1;
967 utf_char = (c & 0x1f);
968 } else if ((c & 0xf0) == 0xe0) {
969 utf_size = utf_state = 2;
970 utf_char = (c & 0x0f);
971 } else if ((c & 0xf8) == 0xf0) {
972 utf_size = utf_state = 3;
973 utf_char = (c & 0x07);
974 } else if ((c & 0xfc) == 0xf8) {
975 utf_size = utf_state = 4;
976 utf_char = (c & 0x03);
977 } else if ((c & 0xfe) == 0xfc) {
978 utf_size = utf_state = 5;
979 utf_char = (c & 0x01);
990 if ((c & 0xC0) != 0x80) {
996 utf_char = (utf_char << 6) | (c & 0x3f);
1002 /* Is somebody trying to be evil! */
1004 (c < 0x800 && utf_size >= 2) ||
1005 (c < 0x10000 && utf_size >= 3) ||
1006 (c < 0x200000 && utf_size >= 4) ||
1007 (c < 0x4000000 && utf_size >= 5))
1010 /* Unicode line separator and paragraph separator are CR-LF */
1011 if (c == 0x2028 || c == 0x2029)
1014 /* High controls are probably a Baaad idea too. */
1018 /* The UTF-16 surrogates are not nice either. */
1019 /* The standard give the option of decoding these:
1020 * I don't want to! */
1021 if (c >= 0xD800 && c < 0xE000)
1024 /* ISO 10646 characters now limited to UTF-16 range. */
1028 /* This is currently a TagPhobic application.. */
1029 if (c >= 0xE0000 && c <= 0xE007F)
1032 /* U+FEFF is best seen as a null. */
1035 /* But U+FFFE is an error. */
1036 if (c == 0xFFFE || c == 0xFFFF)
1039 /* Oops this is a 16bit implementation */
1044 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1046 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1048 if (sco_acs == 2) c ^= 0x80;
1051 switch (cset_attr[cset]) {
1053 * Linedraw characters are different from 'ESC ( B'
1054 * only for a small range. For ones outside that
1055 * range, make sure we use the same font as well as
1056 * the same encoding.
1059 if (unitab_ctrl[c] != 0xFF)
1062 c = ((unsigned char) c) | ATTR_LINEDRW;
1066 /* If UK-ASCII, make the '#' a LineDraw Pound */
1068 c = '}' | ATTR_LINEDRW;
1071 /*FALLTHROUGH*/ case ATTR_ASCII:
1072 if (unitab_ctrl[c] != 0xFF)
1075 c = ((unsigned char) c) | ATTR_ASCII;
1078 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1084 /* How about C1 controls ? */
1085 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1086 has_compat(VT220)) {
1087 termstate = SEEN_ESC;
1089 c = '@' + (c & 0x1F);
1092 /* Or the GL control. */
1093 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1094 if (curs.x && !wrapnext)
1098 *cpos = (' ' | curr_attr | ATTR_ASCII);
1100 /* Or normal C0 controls. */
1101 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1103 case '\005': /* terminal type query */
1104 /* Strictly speaking this is VT100 but a VT100 defaults to
1105 * no response. Other terminals respond at their option.
1107 * Don't put a CR in the default string as this tends to
1108 * upset some weird software.
1110 * An xterm returns "xterm" (5 characters)
1112 compatibility(ANSIMIN);
1114 char abuf[256], *s, *d;
1116 for (s = cfg.answerback, d = abuf; *s; s++) {
1118 if (*s >= 'a' && *s <= 'z')
1119 *d++ = (*s - ('a' - 1));
1120 else if ((*s >= '@' && *s <= '_') ||
1121 *s == '?' || (*s & 0x80))
1126 } else if (*s == '^') {
1131 lpage_send(CP_ACP, abuf, d - abuf);
1136 struct beeptime *newbeep;
1139 ticks = GetTickCount();
1141 if (!beep_overloaded) {
1142 newbeep = smalloc(sizeof(struct beeptime));
1143 newbeep->ticks = ticks;
1144 newbeep->next = NULL;
1148 beeptail->next = newbeep;
1154 * Throw out any beeps that happened more than
1158 beephead->ticks < ticks - cfg.bellovl_t) {
1159 struct beeptime *tmp = beephead;
1160 beephead = tmp->next;
1167 if (cfg.bellovl && beep_overloaded &&
1168 ticks - lastbeep >= cfg.bellovl_s) {
1170 * If we're currently overloaded and the
1171 * last beep was more than s seconds ago,
1172 * leave overload mode.
1174 beep_overloaded = FALSE;
1175 } else if (cfg.bellovl && !beep_overloaded &&
1176 nbeeps >= cfg.bellovl_n) {
1178 * Now, if we have n or more beeps
1179 * remaining in the queue, go into overload
1182 beep_overloaded = TRUE;
1187 * Perform an actual beep if we're not overloaded.
1189 if (!cfg.bellovl || !beep_overloaded) {
1191 if (cfg.beep == BELL_VISUAL) {
1193 vbell_timeout = ticks + VBELL_TIMEOUT;
1201 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1202 else if (curs.x == 0 && curs.y > 0)
1203 curs.x = cols - 1, curs.y--;
1209 seen_disp_event = TRUE;
1212 compatibility(VT100);
1216 compatibility(VT100);
1221 termstate = VT52_ESC;
1223 compatibility(ANSIMIN);
1224 termstate = SEEN_ESC;
1232 seen_disp_event = TRUE;
1234 logtraffic((unsigned char) c, LGTYP_ASCII);
1237 if (has_compat(SCOANSI)) {
1239 erase_lots(FALSE, FALSE, TRUE);
1242 seen_disp_event = 1;
1246 compatibility(VT100);
1248 if (curs.y == marg_b)
1249 scroll(marg_t, marg_b, 1, TRUE);
1250 else if (curs.y < rows - 1)
1256 seen_disp_event = 1;
1258 logtraffic((unsigned char) c, LGTYP_ASCII);
1262 pos old_curs = curs;
1263 unsigned long *ldata = lineptr(curs.y);
1267 } while (curs.x < cols - 1 && !tabs[curs.x]);
1269 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1270 if (curs.x >= cols / 2)
1271 curs.x = cols / 2 - 1;
1278 check_selection(old_curs, curs);
1280 seen_disp_event = TRUE;
1284 switch (termstate) {
1286 /* Only graphic characters get this far, ctrls are stripped above */
1287 if (wrapnext && wrap) {
1288 cpos[1] |= LATTR_WRAPPED;
1289 if (curs.y == marg_b)
1290 scroll(marg_t, marg_b, 1, TRUE);
1291 else if (curs.y < rows - 1)
1299 if (selstate != NO_SELECTION) {
1300 pos cursplus = curs;
1302 check_selection(curs, cursplus);
1304 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1305 logtraffic((unsigned char) c, LGTYP_ASCII);
1307 extern int wcwidth(wchar_t ucs);
1312 width = wcwidth((wchar_t) c);
1315 if (curs.x + 1 != cols) {
1316 *cpos++ = c | ATTR_WIDE | curr_attr;
1317 *cpos++ = UCSWIDE | curr_attr;
1322 *cpos++ = c | curr_attr;
1329 if (curs.x == cols) {
1333 if (wrap && vt52_mode) {
1334 cpos[1] |= LATTR_WRAPPED;
1335 if (curs.y == marg_b)
1336 scroll(marg_t, marg_b, 1, TRUE);
1337 else if (curs.y < rows - 1)
1344 seen_disp_event = 1;
1349 * This state is virtually identical to SEEN_ESC, with the
1350 * exception that we have an OSC sequence in the pipeline,
1351 * and _if_ we see a backslash, we process it.
1355 termstate = TOPLEVEL;
1358 /* else fall through */
1360 if (c >= ' ' && c <= '/') {
1367 termstate = TOPLEVEL;
1368 switch (ANSI(c, esc_query)) {
1369 case '[': /* enter CSI mode */
1370 termstate = SEEN_CSI;
1372 esc_args[0] = ARG_DEFAULT;
1375 case ']': /* xterm escape sequences */
1376 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1377 compatibility(OTHER);
1378 termstate = SEEN_OSC;
1381 case '7': /* save cursor */
1382 compatibility(VT100);
1385 case '8': /* restore cursor */
1386 compatibility(VT100);
1388 seen_disp_event = TRUE;
1391 compatibility(VT100);
1392 app_keypad_keys = TRUE;
1395 compatibility(VT100);
1396 app_keypad_keys = FALSE;
1398 case 'D': /* exactly equivalent to LF */
1399 compatibility(VT100);
1400 if (curs.y == marg_b)
1401 scroll(marg_t, marg_b, 1, TRUE);
1402 else if (curs.y < rows - 1)
1406 seen_disp_event = TRUE;
1408 case 'E': /* exactly equivalent to CR-LF */
1409 compatibility(VT100);
1411 if (curs.y == marg_b)
1412 scroll(marg_t, marg_b, 1, TRUE);
1413 else if (curs.y < rows - 1)
1417 seen_disp_event = TRUE;
1419 case 'M': /* reverse index - backwards LF */
1420 compatibility(VT100);
1421 if (curs.y == marg_t)
1422 scroll(marg_t, marg_b, -1, TRUE);
1423 else if (curs.y > 0)
1427 seen_disp_event = TRUE;
1429 case 'Z': /* terminal type query */
1430 compatibility(VT100);
1431 ldisc_send(id_string, strlen(id_string));
1433 case 'c': /* restore power-on settings */
1434 compatibility(VT100);
1437 request_resize(80, rows, 1);
1442 seen_disp_event = TRUE;
1444 case 'H': /* set a tab */
1445 compatibility(VT100);
1446 tabs[curs.x] = TRUE;
1449 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1450 compatibility(VT100);
1452 unsigned long *ldata;
1456 for (i = 0; i < rows; i++) {
1458 for (j = 0; j < cols; j++)
1459 ldata[j] = ATTR_DEFAULT | 'E';
1463 seen_disp_event = TRUE;
1464 scrtop.x = scrtop.y = 0;
1467 check_selection(scrtop, scrbot);
1471 case ANSI('3', '#'):
1472 case ANSI('4', '#'):
1473 case ANSI('5', '#'):
1474 case ANSI('6', '#'):
1475 compatibility(VT100);
1477 unsigned long nlattr;
1478 unsigned long *ldata;
1479 switch (ANSI(c, esc_query)) {
1480 case ANSI('3', '#'):
1483 case ANSI('4', '#'):
1486 case ANSI('5', '#'):
1487 nlattr = LATTR_NORM;
1489 default: /* spiritually case ANSI('6', '#'): */
1490 nlattr = LATTR_WIDE;
1493 ldata = lineptr(curs.y);
1494 ldata[cols] &= ~LATTR_MODE;
1495 ldata[cols] |= nlattr;
1499 case ANSI('A', '('):
1500 compatibility(VT100);
1501 cset_attr[0] = ATTR_GBCHR;
1503 case ANSI('B', '('):
1504 compatibility(VT100);
1505 cset_attr[0] = ATTR_ASCII;
1507 case ANSI('0', '('):
1508 compatibility(VT100);
1509 cset_attr[0] = ATTR_LINEDRW;
1511 case ANSI('U', '('):
1512 compatibility(OTHER);
1513 cset_attr[0] = ATTR_SCOACS;
1516 case ANSI('A', ')'):
1517 compatibility(VT100);
1518 cset_attr[1] = ATTR_GBCHR;
1520 case ANSI('B', ')'):
1521 compatibility(VT100);
1522 cset_attr[1] = ATTR_ASCII;
1524 case ANSI('0', ')'):
1525 compatibility(VT100);
1526 cset_attr[1] = ATTR_LINEDRW;
1528 case ANSI('U', ')'):
1529 compatibility(OTHER);
1530 cset_attr[1] = ATTR_SCOACS;
1533 case ANSI('8', '%'): /* Old Linux code */
1534 case ANSI('G', '%'):
1535 compatibility(OTHER);
1538 case ANSI('@', '%'):
1539 compatibility(OTHER);
1545 termstate = TOPLEVEL; /* default */
1547 if (esc_nargs <= ARGS_MAX) {
1548 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1549 esc_args[esc_nargs - 1] = 0;
1550 esc_args[esc_nargs - 1] =
1551 10 * esc_args[esc_nargs - 1] + c - '0';
1553 termstate = SEEN_CSI;
1554 } else if (c == ';') {
1555 if (++esc_nargs <= ARGS_MAX)
1556 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1557 termstate = SEEN_CSI;
1558 } else if (c < '@') {
1565 termstate = SEEN_CSI;
1567 switch (ANSI(c, esc_query)) {
1568 case 'A': /* move up N lines */
1569 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1570 seen_disp_event = TRUE;
1572 case 'e': /* move down N lines */
1573 compatibility(ANSI);
1576 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1577 seen_disp_event = TRUE;
1579 case ANSI('c', '>'): /* report xterm version */
1580 compatibility(OTHER);
1581 /* this reports xterm version 136 so that VIM can
1582 use the drag messages from the mouse reporting */
1583 ldisc_send("\033[>0;136;0c", 11);
1585 case 'a': /* move right N cols */
1586 compatibility(ANSI);
1589 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1590 seen_disp_event = TRUE;
1592 case 'D': /* move left N cols */
1593 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1594 seen_disp_event = TRUE;
1596 case 'E': /* move down N lines and CR */
1597 compatibility(ANSI);
1598 move(0, curs.y + def(esc_args[0], 1), 1);
1599 seen_disp_event = TRUE;
1601 case 'F': /* move up N lines and CR */
1602 compatibility(ANSI);
1603 move(0, curs.y - def(esc_args[0], 1), 1);
1604 seen_disp_event = TRUE;
1607 case '`': /* set horizontal posn */
1608 compatibility(ANSI);
1609 move(def(esc_args[0], 1) - 1, curs.y, 0);
1610 seen_disp_event = TRUE;
1612 case 'd': /* set vertical posn */
1613 compatibility(ANSI);
1615 (dec_om ? marg_t : 0) + def(esc_args[0],
1618 seen_disp_event = TRUE;
1621 case 'f': /* set horz and vert posns at once */
1623 esc_args[1] = ARG_DEFAULT;
1624 move(def(esc_args[1], 1) - 1,
1625 (dec_om ? marg_t : 0) + def(esc_args[0],
1628 seen_disp_event = TRUE;
1630 case 'J': /* erase screen or parts of it */
1632 unsigned int i = def(esc_args[0], 0) + 1;
1635 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1638 seen_disp_event = TRUE;
1640 case 'K': /* erase line or parts of it */
1642 unsigned int i = def(esc_args[0], 0) + 1;
1645 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1647 seen_disp_event = TRUE;
1649 case 'L': /* insert lines */
1650 compatibility(VT102);
1651 if (curs.y <= marg_b)
1652 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1655 seen_disp_event = TRUE;
1657 case 'M': /* delete lines */
1658 compatibility(VT102);
1659 if (curs.y <= marg_b)
1660 scroll(curs.y, marg_b, def(esc_args[0], 1),
1663 seen_disp_event = TRUE;
1665 case '@': /* insert chars */
1666 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1667 compatibility(VT102);
1668 insch(def(esc_args[0], 1));
1669 seen_disp_event = TRUE;
1671 case 'P': /* delete chars */
1672 compatibility(VT102);
1673 insch(-def(esc_args[0], 1));
1674 seen_disp_event = TRUE;
1676 case 'c': /* terminal type query */
1677 compatibility(VT100);
1678 /* This is the response for a VT102 */
1679 ldisc_send(id_string, strlen(id_string));
1681 case 'n': /* cursor position query */
1682 if (esc_args[0] == 6) {
1684 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1686 ldisc_send(buf, strlen(buf));
1687 } else if (esc_args[0] == 5) {
1688 ldisc_send("\033[0n", 4);
1691 case 'h': /* toggle modes to high */
1693 compatibility(VT100);
1696 for (i = 0; i < esc_nargs; i++)
1697 toggle_mode(esc_args[i], esc_query, TRUE);
1700 case 'l': /* toggle modes to low */
1702 compatibility(VT100);
1705 for (i = 0; i < esc_nargs; i++)
1706 toggle_mode(esc_args[i], esc_query, FALSE);
1709 case 'g': /* clear tabs */
1710 compatibility(VT100);
1711 if (esc_nargs == 1) {
1712 if (esc_args[0] == 0) {
1713 tabs[curs.x] = FALSE;
1714 } else if (esc_args[0] == 3) {
1716 for (i = 0; i < cols; i++)
1721 case 'r': /* set scroll margins */
1722 compatibility(VT100);
1723 if (esc_nargs <= 2) {
1725 top = def(esc_args[0], 1) - 1;
1726 bot = (esc_nargs <= 1
1728 0 ? rows : def(esc_args[1], rows)) - 1;
1731 /* VTTEST Bug 9 - if region is less than 2 lines
1732 * don't change region.
1734 if (bot - top > 0) {
1739 * I used to think the cursor should be
1740 * placed at the top of the newly marginned
1741 * area. Apparently not: VMS TPU falls over
1744 * Well actually it should for Origin mode - RDB
1746 curs.y = (dec_om ? marg_t : 0);
1748 seen_disp_event = TRUE;
1752 case 'm': /* set graphics rendition */
1755 * A VT100 without the AVO only had one attribute, either
1756 * underline or reverse video depending on the cursor type,
1757 * this was selected by CSI 7m.
1760 * This is sometimes DIM, eg on the GIGI and Linux
1762 * This is sometimes INVIS various ANSI.
1764 * This like 22 disables BOLD, DIM and INVIS
1766 * The ANSI colours appear on any terminal that has colour
1767 * (obviously) but the interaction between sgr0 and the
1768 * colours varies but is usually related to the background
1769 * colour erase item.
1770 * The interaction between colour attributes and the mono
1771 * ones is also very implementation dependent.
1773 * The 39 and 49 attributes are likely to be unimplemented.
1776 for (i = 0; i < esc_nargs; i++) {
1777 switch (def(esc_args[i], 0)) {
1778 case 0: /* restore defaults */
1779 curr_attr = ATTR_DEFAULT;
1781 case 1: /* enable bold */
1782 compatibility(VT100AVO);
1783 curr_attr |= ATTR_BOLD;
1785 case 21: /* (enable double underline) */
1786 compatibility(OTHER);
1787 case 4: /* enable underline */
1788 compatibility(VT100AVO);
1789 curr_attr |= ATTR_UNDER;
1791 case 5: /* enable blink */
1792 compatibility(VT100AVO);
1793 curr_attr |= ATTR_BLINK;
1795 case 7: /* enable reverse video */
1796 curr_attr |= ATTR_REVERSE;
1798 case 10: /* SCO acs off */
1799 compatibility(SCOANSI);
1801 case 11: /* SCO acs on */
1802 compatibility(SCOANSI);
1804 case 12: /* SCO acs on flipped */
1805 compatibility(SCOANSI);
1807 case 22: /* disable bold */
1808 compatibility2(OTHER, VT220);
1809 curr_attr &= ~ATTR_BOLD;
1811 case 24: /* disable underline */
1812 compatibility2(OTHER, VT220);
1813 curr_attr &= ~ATTR_UNDER;
1815 case 25: /* disable blink */
1816 compatibility2(OTHER, VT220);
1817 curr_attr &= ~ATTR_BLINK;
1819 case 27: /* disable reverse video */
1820 compatibility2(OTHER, VT220);
1821 curr_attr &= ~ATTR_REVERSE;
1832 curr_attr &= ~ATTR_FGMASK;
1834 (esc_args[i] - 30) << ATTR_FGSHIFT;
1836 case 39: /* default-foreground */
1837 curr_attr &= ~ATTR_FGMASK;
1838 curr_attr |= ATTR_DEFFG;
1849 curr_attr &= ~ATTR_BGMASK;
1851 (esc_args[i] - 40) << ATTR_BGSHIFT;
1853 case 49: /* default-background */
1854 curr_attr &= ~ATTR_BGMASK;
1855 curr_attr |= ATTR_DEFBG;
1860 erase_char = (' ' | ATTR_ASCII |
1862 (ATTR_FGMASK | ATTR_BGMASK)));
1865 case 's': /* save cursor */
1868 case 'u': /* restore cursor */
1870 seen_disp_event = TRUE;
1872 case 't': /* set page size - ie window height */
1874 * VT340/VT420 sequence DECSLPP, DEC only allows values
1875 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1876 * illegal values (eg first arg 1..9) for window changing
1879 compatibility(VT340TEXT);
1881 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1882 request_resize(cols, def(esc_args[0], 24), 0);
1887 compatibility(SCOANSI);
1888 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1891 seen_disp_event = TRUE;
1894 compatibility(SCOANSI);
1895 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1898 seen_disp_event = TRUE;
1900 case ANSI('|', '*'):
1901 /* VT420 sequence DECSNLS
1902 * Set number of lines on screen
1903 * VT420 uses VGA like hardware and can support any size in
1904 * reasonable range (24..49 AIUI) with no default specified.
1906 compatibility(VT420);
1907 if (esc_nargs == 1 && esc_args[0] > 0) {
1908 request_resize(cols,
1909 def(esc_args[0], cfg.height),
1914 case ANSI('|', '$'):
1915 /* VT340/VT420 sequence DECSCPP
1916 * Set number of columns per page
1917 * Docs imply range is only 80 or 132, but I'll allow any.
1919 compatibility(VT340TEXT);
1920 if (esc_nargs <= 1) {
1921 request_resize(def(esc_args[0], cfg.width),
1926 case 'X': /* write N spaces w/o moving cursor */
1927 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1928 compatibility(ANSIMIN);
1930 int n = def(esc_args[0], 1);
1932 unsigned long *p = cpos;
1933 if (n > cols - curs.x)
1937 check_selection(curs, cursplus);
1940 seen_disp_event = TRUE;
1943 case 'x': /* report terminal characteristics */
1944 compatibility(VT100);
1947 int i = def(esc_args[0], 0);
1948 if (i == 0 || i == 1) {
1949 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1951 ldisc_send(buf, 20);
1955 case 'Z': /* BackTab for xterm */
1956 compatibility(OTHER);
1958 int i = def(esc_args[0], 1);
1959 pos old_curs = curs;
1961 for(;i>0 && curs.x>0; i--) {
1964 } while (curs.x >0 && !tabs[curs.x]);
1967 check_selection(old_curs, curs);
1970 case ANSI('L', '='):
1971 compatibility(OTHER);
1972 use_bce = (esc_args[0] <= 0);
1973 erase_char = ERASE_CHAR;
1975 erase_char = (' ' | ATTR_ASCII |
1977 (ATTR_FGMASK | ATTR_BGMASK)));
1979 case ANSI('E', '='):
1980 compatibility(OTHER);
1981 blink_is_real = (esc_args[0] >= 1);
1983 case ANSI('p', '"'):
1984 /* Allow the host to make this emulator a 'perfect' VT102.
1985 * This first appeared in the VT220, but we do need to get
1986 * back to PuTTY mode so I won't check it.
1988 * The arg in 40..42,50 are a PuTTY extension.
1989 * The 2nd arg, 8bit vs 7bit is not checked.
1991 * Setting VT102 mode should also change the Fkeys to
1992 * generate PF* codes as a real VT102 has no Fkeys.
1993 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1996 * Note ESC c will NOT change this!
1999 switch (esc_args[0]) {
2001 compatibility_level &= ~TM_VTXXX;
2002 compatibility_level |= TM_VT102;
2005 compatibility_level &= ~TM_VTXXX;
2006 compatibility_level |= TM_VT220;
2010 if (esc_args[0] > 60 && esc_args[0] < 70)
2011 compatibility_level |= TM_VTXXX;
2015 compatibility_level &= TM_VTXXX;
2018 compatibility_level = TM_PUTTY;
2021 compatibility_level = TM_SCOANSI;
2025 compatibility_level = TM_PUTTY;
2031 /* Change the response to CSI c */
2032 if (esc_args[0] == 50) {
2035 strcpy(id_string, "\033[?");
2036 for (i = 1; i < esc_nargs; i++) {
2038 strcat(id_string, ";");
2039 sprintf(lbuf, "%d", esc_args[i]);
2040 strcat(id_string, lbuf);
2042 strcat(id_string, "c");
2045 /* Is this a good idea ?
2046 * Well we should do a soft reset at this point ...
2048 if (!has_compat(VT420) && has_compat(VT100)) {
2050 request_resize(132, 24, 1);
2052 request_resize(80, 24, 1);
2061 case 'P': /* Linux palette sequence */
2062 termstate = SEEN_OSC_P;
2065 case 'R': /* Linux palette reset */
2068 termstate = TOPLEVEL;
2070 case 'W': /* word-set */
2071 termstate = SEEN_OSC_W;
2084 esc_args[0] = 10 * esc_args[0] + c - '0';
2088 * Grotty hack to support xterm and DECterm title
2089 * sequences concurrently.
2091 if (esc_args[0] == 2) {
2095 /* else fall through */
2097 termstate = OSC_STRING;
2103 * This OSC stuff is EVIL. It takes just one character to get into
2104 * sysline mode and it's not initially obvious how to get out.
2105 * So I've added CR and LF as string aborts.
2106 * This shouldn't effect compatibility as I believe embedded
2107 * control characters are supposed to be interpreted (maybe?)
2108 * and they don't display anything useful anyway.
2112 if (c == '\n' || c == '\r') {
2113 termstate = TOPLEVEL;
2114 } else if (c == 0234 || c == '\007') {
2116 * These characters terminate the string; ST and BEL
2117 * terminate the sequence and trigger instant
2118 * processing of it, whereas ESC goes back to SEEN_ESC
2119 * mode unless it is followed by \, in which case it is
2120 * synonymous with ST in the first place.
2123 termstate = TOPLEVEL;
2124 } else if (c == '\033')
2125 termstate = OSC_MAYBE_ST;
2126 else if (osc_strlen < OSC_STR_MAX)
2127 osc_string[osc_strlen++] = c;
2131 int max = (osc_strlen == 0 ? 21 : 16);
2133 if (c >= '0' && c <= '9')
2135 else if (c >= 'A' && c <= 'A' + max - 10)
2137 else if (c >= 'a' && c <= 'a' + max - 10)
2140 termstate = TOPLEVEL;
2143 osc_string[osc_strlen++] = val;
2144 if (osc_strlen >= 7) {
2145 palette_set(osc_string[0],
2146 osc_string[1] * 16 + osc_string[2],
2147 osc_string[3] * 16 + osc_string[4],
2148 osc_string[5] * 16 + osc_string[6]);
2150 termstate = TOPLEVEL;
2166 esc_args[0] = 10 * esc_args[0] + c - '0';
2169 termstate = OSC_STRING;
2174 termstate = TOPLEVEL;
2175 seen_disp_event = TRUE;
2178 move(curs.x, curs.y - 1, 1);
2181 move(curs.x, curs.y + 1, 1);
2184 move(curs.x + 1, curs.y, 1);
2187 move(curs.x - 1, curs.y, 1);
2190 * From the VT100 Manual
2191 * NOTE: The special graphics characters in the VT100
2192 * are different from those in the VT52
2194 * From VT102 manual:
2195 * 137 _ Blank - Same
2196 * 140 ` Reserved - Humm.
2197 * 141 a Solid rectangle - Similar
2198 * 142 b 1/ - Top half of fraction for the
2199 * 143 c 3/ - subscript numbers below.
2202 * 146 f Degrees - Same
2203 * 147 g Plus or minus - Same
2205 * 151 i Ellipsis (dots)
2208 * 154 l Bar at scan 0
2209 * 155 m Bar at scan 1
2210 * 156 n Bar at scan 2
2211 * 157 o Bar at scan 3 - Similar
2212 * 160 p Bar at scan 4 - Similar
2213 * 161 q Bar at scan 5 - Similar
2214 * 162 r Bar at scan 6 - Same
2215 * 163 s Bar at scan 7 - Similar
2230 cset_attr[cset = 0] = ATTR_LINEDRW;
2233 cset_attr[cset = 0] = ATTR_ASCII;
2240 scroll(0, rows - 1, -1, TRUE);
2241 else if (curs.y > 0)
2247 erase_lots(FALSE, FALSE, TRUE);
2251 erase_lots(TRUE, FALSE, TRUE);
2255 /* XXX Print cursor line */
2258 /* XXX Start controller mode */
2261 /* XXX Stop controller mode */
2265 termstate = VT52_Y1;
2268 ldisc_send("\033/Z", 3);
2271 app_keypad_keys = TRUE;
2274 app_keypad_keys = FALSE;
2277 /* XXX This should switch to VT100 mode not current or default
2278 * VT mode. But this will only have effect in a VT220+
2282 blink_is_real = cfg.blinktext;
2286 /* XXX Enter auto print mode */
2289 /* XXX Exit auto print mode */
2292 /* XXX Print screen */
2298 /* compatibility(ATARI) */
2300 erase_lots(FALSE, FALSE, TRUE);
2304 /* compatibility(ATARI) */
2305 if (curs.y <= marg_b)
2306 scroll(curs.y, marg_b, -1, FALSE);
2309 /* compatibility(ATARI) */
2310 if (curs.y <= marg_b)
2311 scroll(curs.y, marg_b, 1, TRUE);
2314 /* compatibility(ATARI) */
2315 termstate = VT52_FG;
2318 /* compatibility(ATARI) */
2319 termstate = VT52_BG;
2322 /* compatibility(ATARI) */
2323 erase_lots(FALSE, TRUE, FALSE);
2327 /* compatibility(ATARI) */
2331 /* compatibility(ATARI) */
2334 /* case 'j': Save cursor position - broken on ST */
2335 /* case 'k': Restore cursor position */
2337 /* compatibility(ATARI) */
2338 erase_lots(TRUE, TRUE, TRUE);
2344 /* compatibility(ATARI) */
2345 erase_lots(TRUE, TRUE, FALSE);
2348 /* compatibility(ATARI) */
2349 curr_attr |= ATTR_REVERSE;
2352 /* compatibility(ATARI) */
2353 curr_attr &= ~ATTR_REVERSE;
2355 case 'v': /* wrap Autowrap on - Wyse style */
2356 /* compatibility(ATARI) */
2359 case 'w': /* Autowrap off */
2360 /* compatibility(ATARI) */
2365 /* compatibility(OTHER) */
2367 curr_attr = ATTR_DEFAULT;
2369 erase_char = (' ' | ATTR_ASCII |
2371 (ATTR_FGMASK | ATTR_BGMASK)));
2374 /* compatibility(VI50) */
2375 curr_attr |= ATTR_UNDER;
2378 /* compatibility(VI50) */
2379 curr_attr &= ~ATTR_UNDER;
2382 /* compatibility(VI50) */
2384 curr_attr |= ATTR_BOLD;
2387 /* compatibility(VI50) */
2389 curr_attr &= ~ATTR_BOLD;
2395 termstate = VT52_Y2;
2396 move(curs.x, c - ' ', 0);
2399 termstate = TOPLEVEL;
2400 move(c - ' ', curs.y, 0);
2405 termstate = TOPLEVEL;
2406 curr_attr &= ~ATTR_FGMASK;
2407 curr_attr &= ~ATTR_BOLD;
2408 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2409 if ((c & 0x8) || vt52_bold)
2410 curr_attr |= ATTR_BOLD;
2413 erase_char = (' ' | ATTR_ASCII |
2414 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2417 termstate = TOPLEVEL;
2418 curr_attr &= ~ATTR_BGMASK;
2419 curr_attr &= ~ATTR_BLINK;
2420 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2422 /* Note: bold background */
2424 curr_attr |= ATTR_BLINK;
2427 erase_char = (' ' | ATTR_ASCII |
2428 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2431 default: break; /* placate gcc warning about enum use */
2433 if (selstate != NO_SELECTION) {
2434 pos cursplus = curs;
2436 check_selection(curs, cursplus);
2444 * Compare two lines to determine whether they are sufficiently
2445 * alike to scroll-optimise one to the other. Return the degree of
2448 static int linecmp(unsigned long *a, unsigned long *b)
2452 for (i = n = 0; i < cols; i++)
2453 n += (*a++ == *b++);
2459 * Given a context, update the window. Out of paranoia, we don't
2460 * allow WM_PAINT responses to do scrolling optimisations.
2462 static void do_paint(Context ctx, int may_optimise)
2464 int i, j, our_curs_y;
2465 unsigned long rv, cursor;
2468 long cursor_background = ERASE_CHAR;
2472 * Check the visual bell state.
2475 ticks = GetTickCount();
2476 if (ticks - vbell_timeout >= 0)
2480 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2483 * screen array, disptop, scrtop,
2485 * cfg.blinkpc, blink_is_real, tblinker,
2486 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2489 /* Has the cursor position or type changed ? */
2492 if (blinker || !cfg.blink_cur)
2493 cursor = TATTR_ACTCURS;
2497 cursor = TATTR_PASCURS;
2499 cursor |= TATTR_RIGHTCURS;
2502 our_curs_y = curs.y - disptop;
2504 if (dispcurs && (curstype != cursor ||
2506 disptext + our_curs_y * (cols + 1) + curs.x)) {
2507 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2508 dispcurs[-1] |= ATTR_INVALID;
2509 if ((*dispcurs & ATTR_WIDE))
2510 dispcurs[1] |= ATTR_INVALID;
2511 *dispcurs |= ATTR_INVALID;
2516 /* The normal screen data */
2517 for (i = 0; i < rows; i++) {
2518 unsigned long *ldata;
2520 int idx, dirty_line, dirty_run;
2521 unsigned long attr = 0;
2522 int updated_line = 0;
2525 int last_run_dirty = 0;
2527 scrpos.y = i + disptop;
2528 ldata = lineptr(scrpos.y);
2529 lattr = (ldata[cols] & LATTR_MODE);
2531 idx = i * (cols + 1);
2532 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2533 disptext[idx + cols] = ldata[cols];
2535 for (j = 0; j < cols; j++, idx++) {
2536 unsigned long tattr, tchar;
2537 unsigned long *d = ldata + j;
2541 tchar = (*d & (CHAR_MASK | CSET_MASK));
2542 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2543 switch (tchar & CSET_MASK) {
2545 tchar = unitab_line[tchar & 0xFF];
2548 tchar = unitab_xterm[tchar & 0xFF];
2551 tchar = unitab_scoacs[tchar&0xFF];
2554 tattr |= (tchar & CSET_MASK);
2557 /* Video reversing things */
2559 ^ (posle(selstart, scrpos) &&
2560 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2562 /* 'Real' blinking ? */
2563 if (blink_is_real && (tattr & ATTR_BLINK)) {
2564 if (has_focus && tblinker) {
2566 tattr &= ~CSET_MASK;
2569 tattr &= ~ATTR_BLINK;
2572 /* Cursor here ? Save the 'background' */
2573 if (i == our_curs_y && j == curs.x) {
2574 cursor_background = tattr | tchar;
2575 dispcurs = disptext + idx;
2578 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2581 break_run = (tattr != attr || j - start >= sizeof(ch));
2583 /* Special hack for VT100 Linedraw glyphs */
2584 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2585 && tchar <= 0xBD) break_run = TRUE;
2587 if (!dbcs_screenfont && !dirty_line) {
2588 if ((tchar | tattr) == disptext[idx])
2590 else if (!dirty_run && ccount == 1)
2595 if ((dirty_run || last_run_dirty) && ccount > 0) {
2596 do_text(ctx, start, i, ch, ccount, attr, lattr);
2602 if (dbcs_screenfont)
2603 last_run_dirty = dirty_run;
2604 dirty_run = dirty_line;
2607 if ((tchar | tattr) != disptext[idx])
2609 ch[ccount++] = (char) tchar;
2610 disptext[idx] = tchar | tattr;
2612 /* If it's a wide char step along to the next one. */
2613 if (tattr & ATTR_WIDE) {
2617 /* Cursor is here ? Ouch! */
2618 if (i == our_curs_y && j == curs.x) {
2619 cursor_background = *d;
2620 dispcurs = disptext + idx;
2622 if (disptext[idx] != *d)
2628 if (dirty_run && ccount > 0) {
2629 do_text(ctx, start, i, ch, ccount, attr, lattr);
2633 /* Cursor on this line ? (and changed) */
2634 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2635 ch[0] = (char) (cursor_background & CHAR_MASK);
2636 attr = (cursor_background & ATTR_MASK) | cursor;
2637 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2644 * Flick the switch that says if blinking things should be shown or hidden.
2647 void term_blink(int flg)
2649 static long last_blink = 0;
2650 static long last_tblink = 0;
2651 long now, blink_diff;
2653 now = GetTickCount();
2654 blink_diff = now - last_tblink;
2656 /* Make sure the text blinks no more than 2Hz */
2657 if (blink_diff < 0 || blink_diff > 450) {
2659 tblinker = !tblinker;
2668 blink_diff = now - last_blink;
2670 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2671 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2679 * Invalidate the whole screen so it will be repainted in full.
2681 void term_invalidate(void)
2685 for (i = 0; i < rows * (cols + 1); i++)
2686 disptext[i] = ATTR_INVALID;
2690 * Paint the window in response to a WM_PAINT message.
2692 void term_paint(Context ctx, int l, int t, int r, int b)
2694 int i, j, left, top, right, bottom;
2696 left = l / font_width;
2697 right = (r - 1) / font_width;
2698 top = t / font_height;
2699 bottom = (b - 1) / font_height;
2700 for (i = top; i <= bottom && i < rows; i++) {
2701 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2702 for (j = left; j <= right && j < cols; j++)
2703 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2705 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2706 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2709 /* This should happen soon enough, also for some reason it sometimes
2710 * fails to actually do anything when re-sizing ... painting the wrong
2712 do_paint (ctx, FALSE);
2717 * Attempt to scroll the scrollback. The second parameter gives the
2718 * position we want to scroll to; the first is +1 to denote that
2719 * this position is relative to the beginning of the scrollback, -1
2720 * to denote it is relative to the end, and 0 to denote that it is
2721 * relative to the current position.
2723 void term_scroll(int rel, int where)
2725 int sbtop = -count234(scrollback);
2727 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2728 if (disptop < sbtop)
2736 static void clipme(pos top, pos bottom)
2739 wchar_t *wbptr; /* where next char goes within workbuf */
2740 int wblen = 0; /* workbuf len */
2741 int buflen; /* amount of memory allocated to workbuf */
2743 buflen = 5120; /* Default size */
2744 workbuf = smalloc(buflen * sizeof(wchar_t));
2745 wbptr = workbuf; /* start filling here */
2747 while (poslt(top, bottom)) {
2749 unsigned long *ldata = lineptr(top.y);
2755 if (!(ldata[cols] & LATTR_WRAPPED)) {
2756 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2757 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2758 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2759 && poslt(top, nlpos))
2761 if (poslt(nlpos, bottom))
2764 while (poslt(top, bottom) && poslt(top, nlpos)) {
2767 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2769 wchar_t cbuf[16], *p;
2770 int uc = (ldata[top.x] & 0xFFFF);
2773 if (uc == UCSWIDE) {
2778 switch (uc & CSET_MASK) {
2781 uc = unitab_xterm[uc & 0xFF];
2785 uc = unitab_line[uc & 0xFF];
2788 uc = unitab_scoacs[uc&0xFF];
2791 switch (uc & CSET_MASK) {
2793 uc = unitab_font[uc & 0xFF];
2796 uc = unitab_oemcp[uc & 0xFF];
2800 set = (uc & CSET_MASK);
2801 c = (uc & CHAR_MASK);
2805 if (DIRECT_FONT(uc)) {
2806 if (c >= ' ' && c != 0x7F) {
2807 unsigned char buf[4];
2810 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2812 buf[1] = (unsigned char) ldata[top.x + 1];
2813 rv = MultiByteToWideChar(font_codepage,
2814 0, buf, 2, wbuf, 4);
2818 rv = MultiByteToWideChar(font_codepage,
2819 0, buf, 1, wbuf, 4);
2823 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2830 for (p = cbuf; *p; p++) {
2831 /* Enough overhead for trailing NL and nul */
2832 if (wblen >= buflen - 16) {
2835 sizeof(wchar_t) * (buflen += 100));
2836 wbptr = workbuf + wblen;
2845 for (i = 0; i < sel_nl_sz; i++) {
2847 *wbptr++ = sel_nl[i];
2855 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2856 if (buflen > 0) /* indicates we allocated this buffer */
2860 void term_copyall(void)
2863 top.y = -count234(scrollback);
2869 * The wordness array is mainly for deciding the disposition of the US-ASCII
2872 static int wordtype(int uc)
2875 int start, end, ctype;
2876 } *wptr, ucs_words[] = {
2882 0x037e, 0x037e, 1}, /* Greek question mark */
2884 0x0387, 0x0387, 1}, /* Greek ano teleia */
2886 0x055a, 0x055f, 1}, /* Armenian punctuation */
2888 0x0589, 0x0589, 1}, /* Armenian full stop */
2890 0x0700, 0x070d, 1}, /* Syriac punctuation */
2892 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2894 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2896 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2898 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2900 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2902 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2904 0x2000, 0x200a, 0}, /* Various spaces */
2906 0x2070, 0x207f, 2}, /* superscript */
2908 0x2080, 0x208f, 2}, /* subscript */
2910 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2912 0x3000, 0x3000, 0}, /* ideographic space */
2914 0x3001, 0x3020, 1}, /* ideographic punctuation */
2916 0x303f, 0x309f, 3}, /* Hiragana */
2918 0x30a0, 0x30ff, 3}, /* Katakana */
2920 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2922 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2924 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2926 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2928 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2930 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2932 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2934 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2936 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2941 uc &= (CSET_MASK | CHAR_MASK);
2943 switch (uc & CSET_MASK) {
2945 uc = unitab_xterm[uc & 0xFF];
2948 uc = unitab_line[uc & 0xFF];
2951 uc = unitab_scoacs[uc&0xFF];
2954 switch (uc & CSET_MASK) {
2956 uc = unitab_font[uc & 0xFF];
2959 uc = unitab_oemcp[uc & 0xFF];
2964 return wordness[uc];
2966 for (wptr = ucs_words; wptr->start; wptr++) {
2967 if (uc >= wptr->start && uc <= wptr->end)
2975 * Spread the selection outwards according to the selection mode.
2977 static pos sel_spread_half(pos p, int dir)
2979 unsigned long *ldata;
2982 ldata = lineptr(p.y);
2987 * In this mode, every character is a separate unit, except
2988 * for runs of spaces at the end of a non-wrapping line.
2990 if (!(ldata[cols] & LATTR_WRAPPED)) {
2991 unsigned long *q = ldata + cols;
2992 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2994 if (q == ldata + cols)
2996 if (p.x >= q - ldata)
2997 p.x = (dir == -1 ? q - ldata : cols - 1);
3002 * In this mode, the units are maximal runs of characters
3003 * whose `wordness' has the same value.
3005 wvalue = wordtype(ldata[p.x]);
3007 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3010 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3016 * In this mode, every line is a unit.
3018 p.x = (dir == -1 ? 0 : cols - 1);
3024 static void sel_spread(void)
3026 selstart = sel_spread_half(selstart, -1);
3028 selend = sel_spread_half(selend, +1);
3032 void term_do_paste(void)
3037 get_clip(&data, &len);
3042 sfree(paste_buffer);
3043 paste_pos = paste_hold = paste_len = 0;
3044 paste_buffer = smalloc(len * sizeof(wchar_t));
3047 while (p < data + len) {
3048 while (p < data + len &&
3049 !(p <= data + len - sel_nl_sz &&
3050 !memcmp(p, sel_nl, sizeof(sel_nl))))
3055 for (i = 0; i < p - q; i++) {
3056 paste_buffer[paste_len++] = q[i];
3060 if (p <= data + len - sel_nl_sz &&
3061 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3062 paste_buffer[paste_len++] = '\r';
3068 /* Assume a small paste will be OK in one go. */
3069 if (paste_len < 256) {
3070 luni_send(paste_buffer, paste_len);
3072 sfree(paste_buffer);
3074 paste_pos = paste_hold = paste_len = 0;
3077 get_clip(NULL, NULL);
3080 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3081 int shift, int ctrl)
3084 unsigned long *ldata;
3100 selpoint.y = y + disptop;
3102 ldata = lineptr(selpoint.y);
3103 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3106 if (xterm_mouse && !(cfg.mouse_override && shift)) {
3107 int encstate = 0, r, c;
3109 static int is_down = 0;
3113 encstate = 0x20; /* left button down */
3124 case MBT_WHEEL_DOWN:
3127 default: break; /* placate gcc warning about enum use */
3131 if (xterm_mouse == 1)
3144 default: break; /* placate gcc warning about enum use */
3153 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3154 ldisc_send(abuf, 6);
3158 b = translate_button(b);
3160 if (b == MBT_SELECT && a == MA_CLICK) {
3162 selstate = ABOUT_TO;
3163 selanchor = selpoint;
3165 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3167 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3168 selstate = DRAGGING;
3169 selstart = selanchor = selpoint;
3173 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3174 (b == MBT_EXTEND && a != MA_RELEASE)) {
3175 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3177 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3178 if (posdiff(selpoint, selstart) <
3179 posdiff(selend, selstart) / 2) {
3183 selanchor = selstart;
3185 selstate = DRAGGING;
3187 if (selstate != ABOUT_TO && selstate != DRAGGING)
3188 selanchor = selpoint;
3189 selstate = DRAGGING;
3190 if (poslt(selpoint, selanchor)) {
3191 selstart = selpoint;
3195 selstart = selanchor;
3200 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3201 if (selstate == DRAGGING) {
3203 * We've completed a selection. We now transfer the
3204 * data to the clipboard.
3206 clipme(selstart, selend);
3207 selstate = SELECTED;
3209 selstate = NO_SELECTION;
3210 } else if (b == MBT_PASTE
3211 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3222 sfree(paste_buffer);
3229 static long last_paste = 0;
3230 long now, paste_diff;
3235 /* Don't wait forever to paste */
3237 now = GetTickCount();
3238 paste_diff = now - last_paste;
3239 if (paste_diff >= 0 && paste_diff < 450)
3244 while (paste_pos < paste_len) {
3246 while (n + paste_pos < paste_len) {
3247 if (paste_buffer[paste_pos + n++] == '\r')
3250 luni_send(paste_buffer + paste_pos, n);
3253 if (paste_pos < paste_len) {
3258 sfree(paste_buffer);
3263 static void deselect(void)
3265 selstate = NO_SELECTION;
3266 selstart.x = selstart.y = selend.x = selend.y = 0;
3269 void term_deselect(void)
3275 int term_ldisc(int option)
3277 if (option == LD_ECHO)
3278 return term_echoing;
3279 if (option == LD_EDIT)
3280 return term_editing;
3285 * from_backend(), to get data from the backend for the terminal.
3287 int from_backend(int is_stderr, char *data, int len)
3290 if (inbuf_head >= INBUF_SIZE)
3292 inbuf[inbuf_head++] = *data++;
3296 * We process all stdout/stderr data immediately we receive it,
3297 * and don't return until it's all gone. Therefore, there's no
3298 * reason at all to return anything other than zero from this
3301 * This is a slightly suboptimal way to deal with SSH2 - in
3302 * principle, the window mechanism would allow us to continue
3303 * to accept data on forwarded ports and X connections even
3304 * while the terminal processing was going slowly - but we
3305 * can't do the 100% right thing without moving the terminal
3306 * processing into a separate thread, and that might hurt
3307 * portability. So we manage stdout buffering the old SSH1 way:
3308 * if the terminal processing goes slowly, the whole SSH
3309 * connection stops accepting data until it's ready.
3311 * In practice, I can't imagine this causing serious trouble.
3317 * Log session traffic.
3319 void logtraffic(unsigned char c, int logmode)
3321 if (cfg.logtype > 0) {
3322 if (cfg.logtype == logmode) {
3323 /* deferred open file from pgm start? */
3332 /* open log file append/overwrite mode */
3342 sprintf(writemod, "wb"); /* default to rewrite */
3343 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3347 i = askappend(cfg.logfilename);
3349 writemod[0] = 'a'; /* set append mode */
3350 else if (i == 0) { /* cancelled */
3352 cfg.logtype = 0; /* disable logging */
3357 lgfp = fopen(cfg.logfilename, writemod);
3358 if (lgfp) { /* enter into event log */
3359 sprintf(buf, "%s session log (%s mode) to file : ",
3360 (writemod[0] == 'a') ? "Appending" : "Writing new",
3361 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3362 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3363 /* Make sure we do not exceed the output buffer size */
3364 strncat(buf, cfg.logfilename, 128);
3365 buf[strlen(buf)] = '\0';
3368 /* --- write header line iinto log file */
3369 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3372 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3374 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3378 void logfclose(void)