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 ANSI('L', '='):
1953 compatibility(OTHER);
1954 use_bce = (esc_args[0] <= 0);
1955 erase_char = ERASE_CHAR;
1957 erase_char = (' ' | ATTR_ASCII |
1959 (ATTR_FGMASK | ATTR_BGMASK)));
1961 case ANSI('E', '='):
1962 compatibility(OTHER);
1963 blink_is_real = (esc_args[0] >= 1);
1965 case ANSI('p', '"'):
1966 /* Allow the host to make this emulator a 'perfect' VT102.
1967 * This first appeared in the VT220, but we do need to get
1968 * back to PuTTY mode so I won't check it.
1970 * The arg in 40..42,50 are a PuTTY extension.
1971 * The 2nd arg, 8bit vs 7bit is not checked.
1973 * Setting VT102 mode should also change the Fkeys to
1974 * generate PF* codes as a real VT102 has no Fkeys.
1975 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1978 * Note ESC c will NOT change this!
1981 switch (esc_args[0]) {
1983 compatibility_level &= ~TM_VTXXX;
1984 compatibility_level |= TM_VT102;
1987 compatibility_level &= ~TM_VTXXX;
1988 compatibility_level |= TM_VT220;
1992 if (esc_args[0] > 60 && esc_args[0] < 70)
1993 compatibility_level |= TM_VTXXX;
1997 compatibility_level &= TM_VTXXX;
2000 compatibility_level = TM_PUTTY;
2003 compatibility_level = TM_SCOANSI;
2007 compatibility_level = TM_PUTTY;
2013 /* Change the response to CSI c */
2014 if (esc_args[0] == 50) {
2017 strcpy(id_string, "\033[?");
2018 for (i = 1; i < esc_nargs; i++) {
2020 strcat(id_string, ";");
2021 sprintf(lbuf, "%d", esc_args[i]);
2022 strcat(id_string, lbuf);
2024 strcat(id_string, "c");
2027 /* Is this a good idea ?
2028 * Well we should do a soft reset at this point ...
2030 if (!has_compat(VT420) && has_compat(VT100)) {
2032 request_resize(132, 24, 1);
2034 request_resize(80, 24, 1);
2043 case 'P': /* Linux palette sequence */
2044 termstate = SEEN_OSC_P;
2047 case 'R': /* Linux palette reset */
2050 termstate = TOPLEVEL;
2052 case 'W': /* word-set */
2053 termstate = SEEN_OSC_W;
2066 esc_args[0] = 10 * esc_args[0] + c - '0';
2070 * Grotty hack to support xterm and DECterm title
2071 * sequences concurrently.
2073 if (esc_args[0] == 2) {
2077 /* else fall through */
2079 termstate = OSC_STRING;
2085 * This OSC stuff is EVIL. It takes just one character to get into
2086 * sysline mode and it's not initially obvious how to get out.
2087 * So I've added CR and LF as string aborts.
2088 * This shouldn't effect compatibility as I believe embedded
2089 * control characters are supposed to be interpreted (maybe?)
2090 * and they don't display anything useful anyway.
2094 if (c == '\n' || c == '\r') {
2095 termstate = TOPLEVEL;
2096 } else if (c == 0234 || c == '\007') {
2098 * These characters terminate the string; ST and BEL
2099 * terminate the sequence and trigger instant
2100 * processing of it, whereas ESC goes back to SEEN_ESC
2101 * mode unless it is followed by \, in which case it is
2102 * synonymous with ST in the first place.
2105 termstate = TOPLEVEL;
2106 } else if (c == '\033')
2107 termstate = OSC_MAYBE_ST;
2108 else if (osc_strlen < OSC_STR_MAX)
2109 osc_string[osc_strlen++] = c;
2113 int max = (osc_strlen == 0 ? 21 : 16);
2115 if (c >= '0' && c <= '9')
2117 else if (c >= 'A' && c <= 'A' + max - 10)
2119 else if (c >= 'a' && c <= 'a' + max - 10)
2122 termstate = TOPLEVEL;
2125 osc_string[osc_strlen++] = val;
2126 if (osc_strlen >= 7) {
2127 palette_set(osc_string[0],
2128 osc_string[1] * 16 + osc_string[2],
2129 osc_string[3] * 16 + osc_string[4],
2130 osc_string[5] * 16 + osc_string[6]);
2132 termstate = TOPLEVEL;
2148 esc_args[0] = 10 * esc_args[0] + c - '0';
2151 termstate = OSC_STRING;
2156 termstate = TOPLEVEL;
2157 seen_disp_event = TRUE;
2160 move(curs.x, curs.y - 1, 1);
2163 move(curs.x, curs.y + 1, 1);
2166 move(curs.x + 1, curs.y, 1);
2169 move(curs.x - 1, curs.y, 1);
2172 * From the VT100 Manual
2173 * NOTE: The special graphics characters in the VT100
2174 * are different from those in the VT52
2176 * From VT102 manual:
2177 * 137 _ Blank - Same
2178 * 140 ` Reserved - Humm.
2179 * 141 a Solid rectangle - Similar
2180 * 142 b 1/ - Top half of fraction for the
2181 * 143 c 3/ - subscript numbers below.
2184 * 146 f Degrees - Same
2185 * 147 g Plus or minus - Same
2187 * 151 i Ellipsis (dots)
2190 * 154 l Bar at scan 0
2191 * 155 m Bar at scan 1
2192 * 156 n Bar at scan 2
2193 * 157 o Bar at scan 3 - Similar
2194 * 160 p Bar at scan 4 - Similar
2195 * 161 q Bar at scan 5 - Similar
2196 * 162 r Bar at scan 6 - Same
2197 * 163 s Bar at scan 7 - Similar
2212 cset_attr[cset = 0] = ATTR_LINEDRW;
2215 cset_attr[cset = 0] = ATTR_ASCII;
2222 scroll(0, rows - 1, -1, TRUE);
2223 else if (curs.y > 0)
2229 erase_lots(FALSE, FALSE, TRUE);
2233 erase_lots(TRUE, FALSE, TRUE);
2237 /* XXX Print cursor line */
2240 /* XXX Start controller mode */
2243 /* XXX Stop controller mode */
2247 termstate = VT52_Y1;
2250 ldisc_send("\033/Z", 3);
2253 app_keypad_keys = TRUE;
2256 app_keypad_keys = FALSE;
2259 /* XXX This should switch to VT100 mode not current or default
2260 * VT mode. But this will only have effect in a VT220+
2264 blink_is_real = cfg.blinktext;
2268 /* XXX Enter auto print mode */
2271 /* XXX Exit auto print mode */
2274 /* XXX Print screen */
2280 /* compatibility(ATARI) */
2282 erase_lots(FALSE, FALSE, TRUE);
2286 /* compatibility(ATARI) */
2287 if (curs.y <= marg_b)
2288 scroll(curs.y, marg_b, -1, FALSE);
2291 /* compatibility(ATARI) */
2292 if (curs.y <= marg_b)
2293 scroll(curs.y, marg_b, 1, TRUE);
2296 /* compatibility(ATARI) */
2297 termstate = VT52_FG;
2300 /* compatibility(ATARI) */
2301 termstate = VT52_BG;
2304 /* compatibility(ATARI) */
2305 erase_lots(FALSE, TRUE, FALSE);
2309 /* compatibility(ATARI) */
2313 /* compatibility(ATARI) */
2316 /* case 'j': Save cursor position - broken on ST */
2317 /* case 'k': Restore cursor position */
2319 /* compatibility(ATARI) */
2320 erase_lots(TRUE, TRUE, TRUE);
2326 /* compatibility(ATARI) */
2327 erase_lots(TRUE, TRUE, FALSE);
2330 /* compatibility(ATARI) */
2331 curr_attr |= ATTR_REVERSE;
2334 /* compatibility(ATARI) */
2335 curr_attr &= ~ATTR_REVERSE;
2337 case 'v': /* wrap Autowrap on - Wyse style */
2338 /* compatibility(ATARI) */
2341 case 'w': /* Autowrap off */
2342 /* compatibility(ATARI) */
2347 /* compatibility(OTHER) */
2349 curr_attr = ATTR_DEFAULT;
2351 erase_char = (' ' | ATTR_ASCII |
2353 (ATTR_FGMASK | ATTR_BGMASK)));
2356 /* compatibility(VI50) */
2357 curr_attr |= ATTR_UNDER;
2360 /* compatibility(VI50) */
2361 curr_attr &= ~ATTR_UNDER;
2364 /* compatibility(VI50) */
2366 curr_attr |= ATTR_BOLD;
2369 /* compatibility(VI50) */
2371 curr_attr &= ~ATTR_BOLD;
2377 termstate = VT52_Y2;
2378 move(curs.x, c - ' ', 0);
2381 termstate = TOPLEVEL;
2382 move(c - ' ', curs.y, 0);
2387 termstate = TOPLEVEL;
2388 curr_attr &= ~ATTR_FGMASK;
2389 curr_attr &= ~ATTR_BOLD;
2390 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2391 if ((c & 0x8) || vt52_bold)
2392 curr_attr |= ATTR_BOLD;
2395 erase_char = (' ' | ATTR_ASCII |
2396 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2399 termstate = TOPLEVEL;
2400 curr_attr &= ~ATTR_BGMASK;
2401 curr_attr &= ~ATTR_BLINK;
2402 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2404 /* Note: bold background */
2406 curr_attr |= ATTR_BLINK;
2409 erase_char = (' ' | ATTR_ASCII |
2410 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2413 default: break; /* placate gcc warning about enum use */
2415 if (selstate != NO_SELECTION) {
2416 pos cursplus = curs;
2418 check_selection(curs, cursplus);
2426 * Compare two lines to determine whether they are sufficiently
2427 * alike to scroll-optimise one to the other. Return the degree of
2430 static int linecmp(unsigned long *a, unsigned long *b)
2434 for (i = n = 0; i < cols; i++)
2435 n += (*a++ == *b++);
2441 * Given a context, update the window. Out of paranoia, we don't
2442 * allow WM_PAINT responses to do scrolling optimisations.
2444 static void do_paint(Context ctx, int may_optimise)
2446 int i, j, our_curs_y;
2447 unsigned long rv, cursor;
2450 long cursor_background = ERASE_CHAR;
2454 * Check the visual bell state.
2457 ticks = GetTickCount();
2458 if (ticks - vbell_timeout >= 0)
2462 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2465 * screen array, disptop, scrtop,
2467 * cfg.blinkpc, blink_is_real, tblinker,
2468 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2471 /* Has the cursor position or type changed ? */
2474 if (blinker || !cfg.blink_cur)
2475 cursor = TATTR_ACTCURS;
2479 cursor = TATTR_PASCURS;
2481 cursor |= TATTR_RIGHTCURS;
2484 our_curs_y = curs.y - disptop;
2486 if (dispcurs && (curstype != cursor ||
2488 disptext + our_curs_y * (cols + 1) + curs.x)) {
2489 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2490 dispcurs[-1] |= ATTR_INVALID;
2491 if ((*dispcurs & ATTR_WIDE))
2492 dispcurs[1] |= ATTR_INVALID;
2493 *dispcurs |= ATTR_INVALID;
2498 /* The normal screen data */
2499 for (i = 0; i < rows; i++) {
2500 unsigned long *ldata;
2502 int idx, dirty_line, dirty_run;
2503 unsigned long attr = 0;
2504 int updated_line = 0;
2507 int last_run_dirty = 0;
2509 scrpos.y = i + disptop;
2510 ldata = lineptr(scrpos.y);
2511 lattr = (ldata[cols] & LATTR_MODE);
2513 idx = i * (cols + 1);
2514 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2515 disptext[idx + cols] = ldata[cols];
2517 for (j = 0; j < cols; j++, idx++) {
2518 unsigned long tattr, tchar;
2519 unsigned long *d = ldata + j;
2523 tchar = (*d & (CHAR_MASK | CSET_MASK));
2524 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2525 switch (tchar & CSET_MASK) {
2527 tchar = unitab_line[tchar & 0xFF];
2530 tchar = unitab_xterm[tchar & 0xFF];
2533 tchar = unitab_scoacs[tchar&0xFF];
2536 tattr |= (tchar & CSET_MASK);
2539 /* Video reversing things */
2541 ^ (posle(selstart, scrpos) &&
2542 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2544 /* 'Real' blinking ? */
2545 if (blink_is_real && (tattr & ATTR_BLINK)) {
2546 if (has_focus && tblinker) {
2548 tattr &= ~CSET_MASK;
2551 tattr &= ~ATTR_BLINK;
2554 /* Cursor here ? Save the 'background' */
2555 if (i == our_curs_y && j == curs.x) {
2556 cursor_background = tattr | tchar;
2557 dispcurs = disptext + idx;
2560 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2563 break_run = (tattr != attr || j - start >= sizeof(ch));
2565 /* Special hack for VT100 Linedraw glyphs */
2566 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2567 && tchar <= 0xBD) break_run = TRUE;
2569 if (!dbcs_screenfont && !dirty_line) {
2570 if ((tchar | tattr) == disptext[idx])
2572 else if (!dirty_run && ccount == 1)
2577 if ((dirty_run || last_run_dirty) && ccount > 0) {
2578 do_text(ctx, start, i, ch, ccount, attr, lattr);
2584 if (dbcs_screenfont)
2585 last_run_dirty = dirty_run;
2586 dirty_run = dirty_line;
2589 if ((tchar | tattr) != disptext[idx])
2591 ch[ccount++] = (char) tchar;
2592 disptext[idx] = tchar | tattr;
2594 /* If it's a wide char step along to the next one. */
2595 if (tattr & ATTR_WIDE) {
2599 /* Cursor is here ? Ouch! */
2600 if (i == our_curs_y && j == curs.x) {
2601 cursor_background = *d;
2602 dispcurs = disptext + idx;
2604 if (disptext[idx] != *d)
2610 if (dirty_run && ccount > 0) {
2611 do_text(ctx, start, i, ch, ccount, attr, lattr);
2615 /* Cursor on this line ? (and changed) */
2616 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2617 ch[0] = (char) (cursor_background & CHAR_MASK);
2618 attr = (cursor_background & ATTR_MASK) | cursor;
2619 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2626 * Flick the switch that says if blinking things should be shown or hidden.
2629 void term_blink(int flg)
2631 static long last_blink = 0;
2632 static long last_tblink = 0;
2633 long now, blink_diff;
2635 now = GetTickCount();
2636 blink_diff = now - last_tblink;
2638 /* Make sure the text blinks no more than 2Hz */
2639 if (blink_diff < 0 || blink_diff > 450) {
2641 tblinker = !tblinker;
2650 blink_diff = now - last_blink;
2652 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2653 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2661 * Invalidate the whole screen so it will be repainted in full.
2663 void term_invalidate(void)
2667 for (i = 0; i < rows * (cols + 1); i++)
2668 disptext[i] = ATTR_INVALID;
2672 * Paint the window in response to a WM_PAINT message.
2674 void term_paint(Context ctx, int l, int t, int r, int b)
2676 int i, j, left, top, right, bottom;
2678 left = l / font_width;
2679 right = (r - 1) / font_width;
2680 top = t / font_height;
2681 bottom = (b - 1) / font_height;
2682 for (i = top; i <= bottom && i < rows; i++) {
2683 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2684 for (j = left; j <= right && j < cols; j++)
2685 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2687 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2688 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2691 /* This should happen soon enough, also for some reason it sometimes
2692 * fails to actually do anything when re-sizing ... painting the wrong
2694 do_paint (ctx, FALSE);
2699 * Attempt to scroll the scrollback. The second parameter gives the
2700 * position we want to scroll to; the first is +1 to denote that
2701 * this position is relative to the beginning of the scrollback, -1
2702 * to denote it is relative to the end, and 0 to denote that it is
2703 * relative to the current position.
2705 void term_scroll(int rel, int where)
2707 int sbtop = -count234(scrollback);
2709 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2710 if (disptop < sbtop)
2718 static void clipme(pos top, pos bottom)
2721 wchar_t *wbptr; /* where next char goes within workbuf */
2722 int wblen = 0; /* workbuf len */
2723 int buflen; /* amount of memory allocated to workbuf */
2725 buflen = 5120; /* Default size */
2726 workbuf = smalloc(buflen * sizeof(wchar_t));
2727 wbptr = workbuf; /* start filling here */
2729 while (poslt(top, bottom)) {
2731 unsigned long *ldata = lineptr(top.y);
2737 if (!(ldata[cols] & LATTR_WRAPPED)) {
2738 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2739 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2740 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2741 && poslt(top, nlpos))
2743 if (poslt(nlpos, bottom))
2746 while (poslt(top, bottom) && poslt(top, nlpos)) {
2749 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2751 wchar_t cbuf[16], *p;
2752 int uc = (ldata[top.x] & 0xFFFF);
2755 if (uc == UCSWIDE) {
2760 switch (uc & CSET_MASK) {
2763 uc = unitab_xterm[uc & 0xFF];
2767 uc = unitab_line[uc & 0xFF];
2770 uc = unitab_scoacs[uc&0xFF];
2773 switch (uc & CSET_MASK) {
2775 uc = unitab_font[uc & 0xFF];
2778 uc = unitab_oemcp[uc & 0xFF];
2782 set = (uc & CSET_MASK);
2783 c = (uc & CHAR_MASK);
2787 if (DIRECT_FONT(uc)) {
2788 if (c >= ' ' && c != 0x7F) {
2789 unsigned char buf[4];
2792 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2794 buf[1] = (unsigned char) ldata[top.x + 1];
2795 rv = MultiByteToWideChar(font_codepage,
2796 0, buf, 2, wbuf, 4);
2800 rv = MultiByteToWideChar(font_codepage,
2801 0, buf, 1, wbuf, 4);
2805 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2812 for (p = cbuf; *p; p++) {
2813 /* Enough overhead for trailing NL and nul */
2814 if (wblen >= buflen - 16) {
2817 sizeof(wchar_t) * (buflen += 100));
2818 wbptr = workbuf + wblen;
2827 for (i = 0; i < sel_nl_sz; i++) {
2829 *wbptr++ = sel_nl[i];
2837 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2838 if (buflen > 0) /* indicates we allocated this buffer */
2842 void term_copyall(void)
2845 top.y = -count234(scrollback);
2851 * The wordness array is mainly for deciding the disposition of the US-ASCII
2854 static int wordtype(int uc)
2857 int start, end, ctype;
2858 } *wptr, ucs_words[] = {
2864 0x037e, 0x037e, 1}, /* Greek question mark */
2866 0x0387, 0x0387, 1}, /* Greek ano teleia */
2868 0x055a, 0x055f, 1}, /* Armenian punctuation */
2870 0x0589, 0x0589, 1}, /* Armenian full stop */
2872 0x0700, 0x070d, 1}, /* Syriac punctuation */
2874 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2876 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2878 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2880 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2882 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2884 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2886 0x2000, 0x200a, 0}, /* Various spaces */
2888 0x2070, 0x207f, 2}, /* superscript */
2890 0x2080, 0x208f, 2}, /* subscript */
2892 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2894 0x3000, 0x3000, 0}, /* ideographic space */
2896 0x3001, 0x3020, 1}, /* ideographic punctuation */
2898 0x303f, 0x309f, 3}, /* Hiragana */
2900 0x30a0, 0x30ff, 3}, /* Katakana */
2902 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2904 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2906 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2908 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2910 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2912 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2914 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2916 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2918 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2923 uc &= (CSET_MASK | CHAR_MASK);
2925 switch (uc & CSET_MASK) {
2927 uc = unitab_xterm[uc & 0xFF];
2930 uc = unitab_line[uc & 0xFF];
2933 uc = unitab_scoacs[uc&0xFF];
2936 switch (uc & CSET_MASK) {
2938 uc = unitab_font[uc & 0xFF];
2941 uc = unitab_oemcp[uc & 0xFF];
2946 return wordness[uc];
2948 for (wptr = ucs_words; wptr->start; wptr++) {
2949 if (uc >= wptr->start && uc <= wptr->end)
2957 * Spread the selection outwards according to the selection mode.
2959 static pos sel_spread_half(pos p, int dir)
2961 unsigned long *ldata;
2964 ldata = lineptr(p.y);
2969 * In this mode, every character is a separate unit, except
2970 * for runs of spaces at the end of a non-wrapping line.
2972 if (!(ldata[cols] & LATTR_WRAPPED)) {
2973 unsigned long *q = ldata + cols;
2974 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2976 if (q == ldata + cols)
2978 if (p.x >= q - ldata)
2979 p.x = (dir == -1 ? q - ldata : cols - 1);
2984 * In this mode, the units are maximal runs of characters
2985 * whose `wordness' has the same value.
2987 wvalue = wordtype(ldata[p.x]);
2989 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2992 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2998 * In this mode, every line is a unit.
3000 p.x = (dir == -1 ? 0 : cols - 1);
3006 static void sel_spread(void)
3008 selstart = sel_spread_half(selstart, -1);
3010 selend = sel_spread_half(selend, +1);
3014 void term_do_paste(void)
3019 get_clip(&data, &len);
3024 sfree(paste_buffer);
3025 paste_pos = paste_hold = paste_len = 0;
3026 paste_buffer = smalloc(len * sizeof(wchar_t));
3029 while (p < data + len) {
3030 while (p < data + len &&
3031 !(p <= data + len - sel_nl_sz &&
3032 !memcmp(p, sel_nl, sizeof(sel_nl))))
3037 for (i = 0; i < p - q; i++) {
3038 paste_buffer[paste_len++] = q[i];
3042 if (p <= data + len - sel_nl_sz &&
3043 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3044 paste_buffer[paste_len++] = '\r';
3050 /* Assume a small paste will be OK in one go. */
3051 if (paste_len < 256) {
3052 luni_send(paste_buffer, paste_len);
3054 sfree(paste_buffer);
3056 paste_pos = paste_hold = paste_len = 0;
3059 get_clip(NULL, NULL);
3062 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3063 int shift, int ctrl)
3066 unsigned long *ldata;
3082 selpoint.y = y + disptop;
3084 ldata = lineptr(selpoint.y);
3085 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3089 int encstate = 0, r, c;
3091 static int is_down = 0;
3095 encstate = 0x20; /* left button down */
3106 case MBT_WHEEL_DOWN:
3109 default: break; /* placate gcc warning about enum use */
3113 if (xterm_mouse == 1)
3126 default: break; /* placate gcc warning about enum use */
3135 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3136 ldisc_send(abuf, 6);
3140 b = translate_button(b);
3142 if (b == MBT_SELECT && a == MA_CLICK) {
3144 selstate = ABOUT_TO;
3145 selanchor = selpoint;
3147 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3149 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3150 selstate = DRAGGING;
3151 selstart = selanchor = selpoint;
3155 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3156 (b == MBT_EXTEND && a != MA_RELEASE)) {
3157 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3159 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3160 if (posdiff(selpoint, selstart) <
3161 posdiff(selend, selstart) / 2) {
3165 selanchor = selstart;
3167 selstate = DRAGGING;
3169 if (selstate != ABOUT_TO && selstate != DRAGGING)
3170 selanchor = selpoint;
3171 selstate = DRAGGING;
3172 if (poslt(selpoint, selanchor)) {
3173 selstart = selpoint;
3177 selstart = selanchor;
3182 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3183 if (selstate == DRAGGING) {
3185 * We've completed a selection. We now transfer the
3186 * data to the clipboard.
3188 clipme(selstart, selend);
3189 selstate = SELECTED;
3191 selstate = NO_SELECTION;
3192 } else if (b == MBT_PASTE
3193 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3204 sfree(paste_buffer);
3211 static long last_paste = 0;
3212 long now, paste_diff;
3217 /* Don't wait forever to paste */
3219 now = GetTickCount();
3220 paste_diff = now - last_paste;
3221 if (paste_diff >= 0 && paste_diff < 450)
3226 while (paste_pos < paste_len) {
3228 while (n + paste_pos < paste_len) {
3229 if (paste_buffer[paste_pos + n++] == '\r')
3232 luni_send(paste_buffer + paste_pos, n);
3235 if (paste_pos < paste_len) {
3240 sfree(paste_buffer);
3245 static void deselect(void)
3247 selstate = NO_SELECTION;
3248 selstart.x = selstart.y = selend.x = selend.y = 0;
3251 void term_deselect(void)
3257 int term_ldisc(int option)
3259 if (option == LD_ECHO)
3260 return term_echoing;
3261 if (option == LD_EDIT)
3262 return term_editing;
3267 * from_backend(), to get data from the backend for the terminal.
3269 void from_backend(int is_stderr, char *data, int len)
3272 if (inbuf_head >= INBUF_SIZE)
3274 inbuf[inbuf_head++] = *data++;
3279 * Log session traffic.
3281 void logtraffic(unsigned char c, int logmode)
3283 if (cfg.logtype > 0) {
3284 if (cfg.logtype == logmode) {
3285 /* deferred open file from pgm start? */
3294 /* open log file append/overwrite mode */
3304 sprintf(writemod, "wb"); /* default to rewrite */
3305 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3309 i = askappend(cfg.logfilename);
3311 writemod[0] = 'a'; /* set append mode */
3312 else if (i == 0) { /* cancelled */
3314 cfg.logtype = 0; /* disable logging */
3319 lgfp = fopen(cfg.logfilename, writemod);
3320 if (lgfp) { /* enter into event log */
3321 sprintf(buf, "%s session log (%s mode) to file : ",
3322 (writemod[0] == 'a') ? "Appending" : "Writing new",
3323 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3324 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3325 /* Make sure we do not exceed the output buffer size */
3326 strncat(buf, cfg.logfilename, 128);
3327 buf[strlen(buf)] = '\0';
3330 /* --- write header line iinto log file */
3331 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3334 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3336 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3340 void logfclose(void)