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);
262 #define lineptr(x) lineptr(x,__LINE__)
264 * Set up power-on settings for the terminal.
266 static void power_on(void)
268 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
271 alt_b = marg_b = rows - 1;
276 for (i = 0; i < cols; i++)
277 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
279 alt_om = dec_om = cfg.dec_om;
280 alt_wnext = wrapnext = alt_ins = insert = FALSE;
281 alt_wrap = wrap = cfg.wrap_mode;
284 alt_sco_acs = sco_acs = 0;
285 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
290 save_attr = curr_attr = ATTR_DEFAULT;
291 term_editing = term_echoing = FALSE;
292 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
293 app_cursor_keys = cfg.app_cursor;
294 app_keypad_keys = cfg.app_keypad;
296 blink_is_real = cfg.blinktext;
297 erase_char = ERASE_CHAR;
301 for (i = 0; i < 256; i++)
302 wordness[i] = cfg.wordness[i];
306 erase_lots(FALSE, TRUE, TRUE);
308 erase_lots(FALSE, TRUE, TRUE);
313 * Force a screen update.
315 void term_update(void)
322 if ((seen_key_event && (cfg.scroll_on_key)) ||
323 (seen_disp_event && (cfg.scroll_on_disp))) {
324 disptop = 0; /* return to main screen */
325 seen_disp_event = seen_key_event = 0;
328 sys_cursor(curs.x, curs.y - disptop);
334 * Same as power_on(), but an external function.
336 void term_pwron(void)
346 * Clear the scrollback.
348 void term_clrsb(void)
352 while ((line = delpos234(scrollback, 0)) != NULL) {
359 * Initialise the terminal.
363 screen = alt_screen = scrollback = NULL;
365 disptext = dispcurs = NULL;
370 beephead = beeptail = NULL;
373 beep_overloaded = FALSE;
377 * Set up the terminal for a given size.
379 void term_size(int newrows, int newcols, int newsavelines)
382 unsigned long *newdisp, *line;
385 int save_alt_which = alt_which;
387 if (newrows == rows && newcols == cols && newsavelines == savelines)
388 return; /* nothing to do */
394 alt_b = marg_b = newrows - 1;
397 scrollback = newtree234(NULL);
398 screen = newtree234(NULL);
403 * Resize the screen and scrollback. We only need to shift
404 * lines around within our data structures, because lineptr()
405 * will take care of resizing each individual line if
408 * - If the new screen and the old screen differ in length, we
409 * must shunt some lines in from the scrollback or out to
412 * - If doing that fails to provide us with enough material to
413 * fill the new screen (i.e. the number of rows needed in
414 * the new screen exceeds the total number in the previous
415 * screen+scrollback), we must invent some blank lines to
418 * - Then, if the new scrollback length is less than the
419 * amount of scrollback we actually have, we must throw some
422 sblen = count234(scrollback);
423 /* Do this loop to expand the screen if newrows > rows */
424 for (i = rows; i < newrows; i++) {
426 line = delpos234(scrollback, --sblen);
428 line = smalloc(TSIZE * (newcols + 2));
430 for (j = 0; j <= newcols; j++)
431 line[j + 1] = ERASE_CHAR;
433 addpos234(screen, line, 0);
435 /* Do this loop to shrink the screen if newrows < rows */
436 for (i = newrows; i < rows; i++) {
437 line = delpos234(screen, 0);
438 addpos234(scrollback, line, sblen++);
440 assert(count234(screen) == newrows);
441 while (sblen > newsavelines) {
442 line = delpos234(scrollback, 0);
446 assert(count234(scrollback) <= newsavelines);
449 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
450 for (i = 0; i < newrows * (newcols + 1); i++)
451 newdisp[i] = ATTR_INVALID;
456 newalt = newtree234(NULL);
457 for (i = 0; i < newrows; i++) {
458 line = smalloc(TSIZE * (newcols + 2));
460 for (j = 0; j <= newcols; j++)
461 line[j + 1] = erase_char;
462 addpos234(newalt, line, i);
465 while (NULL != (line = delpos234(alt_screen, 0)))
467 freetree234(alt_screen);
471 tabs = srealloc(tabs, newcols * sizeof(*tabs));
474 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
475 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
479 curs.y += newrows - rows;
482 if (curs.y >= newrows)
483 curs.y = newrows - 1;
484 if (curs.x >= newcols)
485 curs.x = newcols - 1;
487 wrapnext = alt_wnext = FALSE;
491 savelines = newsavelines;
494 swap_screen(save_alt_which);
503 static void swap_screen(int which)
508 if (which == alt_which)
535 wrapnext = alt_wnext;
547 sco_acs = alt_sco_acs;
554 * Update the scroll bar.
556 static void update_sbar(void)
560 nscroll = count234(scrollback);
562 set_sbar(nscroll + rows, nscroll + disptop, rows);
566 * Check whether the region bounded by the two pointers intersects
567 * the scroll region, and de-select the on-screen selection if so.
569 static void check_selection(pos from, pos to)
571 if (poslt(from, selend) && poslt(selstart, to))
576 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
577 * for backward.) `sb' is TRUE if the scrolling is permitted to
578 * affect the scrollback buffer.
580 * NB this function invalidates all pointers into lines of the
581 * screen data structures. In particular, you MUST call fix_cpos
582 * after calling scroll() and before doing anything else that
583 * uses the cpos shortcut pointer.
585 static void scroll(int topline, int botline, int lines, int sb)
587 unsigned long *line, *line2;
590 if (topline != 0 || alt_which != 0)
595 line = delpos234(screen, botline);
596 line = resizeline(line, cols);
597 for (i = 0; i < cols; i++)
598 line[i + 1] = erase_char;
600 addpos234(screen, line, topline);
602 if (selstart.y >= topline && selstart.y <= botline) {
604 if (selstart.y > botline) {
605 selstart.y = botline;
609 if (selend.y >= topline && selend.y <= botline) {
611 if (selend.y > botline) {
621 line = delpos234(screen, topline);
622 if (sb && savelines > 0) {
623 int sblen = count234(scrollback);
625 * We must add this line to the scrollback. We'll
626 * remove a line from the top of the scrollback to
627 * replace it, or allocate a new one if the
628 * scrollback isn't full.
630 if (sblen == savelines) {
631 sblen--, line2 = delpos234(scrollback, 0);
633 line2 = smalloc(TSIZE * (cols + 2));
636 addpos234(scrollback, line, sblen);
639 line = resizeline(line, cols);
640 for (i = 0; i < cols; i++)
641 line[i + 1] = erase_char;
643 addpos234(screen, line, botline);
645 if (selstart.y >= topline && selstart.y <= botline) {
647 if (selstart.y < topline) {
648 selstart.y = topline;
652 if (selend.y >= topline && selend.y <= botline) {
654 if (selend.y < topline) {
666 * Move the cursor to a given position, clipping at boundaries. We
667 * may or may not want to clip at the scroll margin: marg_clip is 0
668 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
669 * even _being_ outside the margins.
671 static void move(int x, int y, int marg_clip)
678 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
680 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
694 * Save or restore the cursor and SGR mode.
696 static void save_cursor(int save)
700 save_attr = curr_attr;
703 save_csattr = cset_attr[cset];
704 save_sco_acs = sco_acs;
707 /* Make sure the window hasn't shrunk since the save */
713 curr_attr = save_attr;
716 cset_attr[cset] = save_csattr;
717 sco_acs = save_sco_acs;
720 erase_char = (' ' | ATTR_ASCII |
721 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
726 * Erase a large portion of the screen: the whole screen, or the
727 * whole line, or parts thereof.
729 static void erase_lots(int line_only, int from_begin, int to_end)
733 unsigned long *ldata;
755 check_selection(start, end);
757 /* Clear screen also forces a full window redraw, just in case. */
758 if (start.y == 0 && start.x == 0 && end.y == rows)
761 ldata = lineptr(start.y);
762 while (poslt(start, end)) {
763 if (start.x == cols && !erase_lattr)
764 ldata[start.x] &= ~LATTR_WRAPPED;
766 ldata[start.x] = erase_char;
767 if (incpos(start) && start.y < rows)
768 ldata = lineptr(start.y);
773 * Insert or delete characters within the current line. n is +ve if
774 * insertion is desired, and -ve for deletion.
776 static void insch(int n)
778 int dir = (n < 0 ? -1 : +1);
781 unsigned long *ldata;
783 n = (n < 0 ? -n : n);
784 if (n > cols - curs.x)
786 m = cols - curs.x - n;
788 cursplus.x = curs.x + n;
789 check_selection(curs, cursplus);
790 ldata = lineptr(curs.y);
792 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
794 ldata[curs.x + m++] = erase_char;
796 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
798 ldata[curs.x + n] = erase_char;
803 * Toggle terminal mode `mode' to state `state'. (`query' indicates
804 * whether the mode is a DEC private one or a normal one.)
806 static void toggle_mode(int mode, int query, int state)
812 case 1: /* application cursor keys */
813 app_cursor_keys = state;
815 case 2: /* VT52 mode */
818 blink_is_real = FALSE;
821 blink_is_real = cfg.blinktext;
824 case 3: /* 80/132 columns */
826 request_resize(state ? 132 : 80, rows, 1);
829 case 5: /* reverse video */
831 * Toggle reverse video. If we receive an OFF within the
832 * visual bell timeout period after an ON, we trigger an
833 * effective visual bell, so that ESC[?5hESC[?5l will
834 * always be an actually _visible_ visual bell.
836 ticks = GetTickCount();
837 if (rvideo && !state && /* we're turning it off */
838 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
839 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
840 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
841 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
842 } else if (!rvideo && state) {
843 /* This is an ON, so we notice the time and save it. */
844 rvbell_timeout = ticks + VBELL_TIMEOUT;
847 seen_disp_event = TRUE;
851 case 6: /* DEC origin mode */
854 case 7: /* auto wrap */
857 case 8: /* auto key repeat */
860 case 10: /* set local edit mode */
861 term_editing = state;
862 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
864 case 25: /* enable/disable cursor */
865 compatibility2(OTHER, VT220);
867 seen_disp_event = TRUE;
869 case 47: /* alternate screen */
870 compatibility(OTHER);
875 case 1000: /* xterm mouse 1 */
876 xterm_mouse = state ? 1 : 0;
877 set_raw_mouse_mode(state);
879 case 1002: /* xterm mouse 2 */
880 xterm_mouse = state ? 2 : 0;
881 set_raw_mouse_mode(state);
885 case 4: /* set insert mode */
886 compatibility(VT102);
889 case 12: /* set echo mode */
890 term_echoing = !state;
891 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
893 case 20: /* Return sends ... */
894 cr_lf_return = state;
896 case 34: /* Make cursor BIG */
897 compatibility2(OTHER, VT220);
903 * Process an OSC sequence: set window title or icon name.
905 static void do_osc(void)
909 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
911 osc_string[osc_strlen] = '\0';
912 switch (esc_args[0]) {
915 set_icon(osc_string);
916 if (esc_args[0] == 1)
918 /* fall through: parameter 0 means set both */
921 set_title(osc_string);
928 * Remove everything currently in `inbuf' and stick it up on the
929 * in-memory display. There's a big state machine in here to
930 * process escape sequences...
936 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
937 c = inbuf[inbuf_reap];
940 * Optionally log the session traffic to a file. Useful for
941 * debugging and possibly also useful for actual logging.
943 logtraffic((unsigned char) c, LGTYP_DEBUG);
945 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
946 * be able to display 8-bit characters, but I'll let that go 'cause
950 /* First see about all those translations. */
951 if (termstate == TOPLEVEL) {
956 /* UTF-8 must be stateless so we ignore iso2022. */
957 if (unitab_ctrl[c] != 0xFF)
959 else c = ((unsigned char)c) | ATTR_ASCII;
961 } else if ((c & 0xe0) == 0xc0) {
962 utf_size = utf_state = 1;
963 utf_char = (c & 0x1f);
964 } else if ((c & 0xf0) == 0xe0) {
965 utf_size = utf_state = 2;
966 utf_char = (c & 0x0f);
967 } else if ((c & 0xf8) == 0xf0) {
968 utf_size = utf_state = 3;
969 utf_char = (c & 0x07);
970 } else if ((c & 0xfc) == 0xf8) {
971 utf_size = utf_state = 4;
972 utf_char = (c & 0x03);
973 } else if ((c & 0xfe) == 0xfc) {
974 utf_size = utf_state = 5;
975 utf_char = (c & 0x01);
986 if ((c & 0xC0) != 0x80) {
987 inbuf_reap--; /* This causes the faulting character */
988 c = UCSERR; /* to be logged twice - not really a */
989 utf_state = 0; /* serious problem. */
992 utf_char = (utf_char << 6) | (c & 0x3f);
998 /* Is somebody trying to be evil! */
1000 (c < 0x800 && utf_size >= 2) ||
1001 (c < 0x10000 && utf_size >= 3) ||
1002 (c < 0x200000 && utf_size >= 4) ||
1003 (c < 0x4000000 && utf_size >= 5))
1006 /* Unicode line separator and paragraph separator are CR-LF */
1007 if (c == 0x2028 || c == 0x2029)
1010 /* High controls are probably a Baaad idea too. */
1014 /* The UTF-16 surrogates are not nice either. */
1015 /* The standard give the option of decoding these:
1016 * I don't want to! */
1017 if (c >= 0xD800 && c < 0xE000)
1020 /* ISO 10646 characters now limited to UTF-16 range. */
1024 /* This is currently a TagPhobic application.. */
1025 if (c >= 0xE0000 && c <= 0xE007F)
1028 /* U+FEFF is best seen as a null. */
1031 /* But U+FFFE is an error. */
1032 if (c == 0xFFFE || c == 0xFFFF)
1035 /* Oops this is a 16bit implementation */
1040 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1042 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1044 if (sco_acs == 2) c ^= 0x80;
1047 switch (cset_attr[cset]) {
1049 * Linedraw characters are different from 'ESC ( B'
1050 * only for a small range. For ones outside that
1051 * range, make sure we use the same font as well as
1052 * the same encoding.
1055 if (unitab_ctrl[c] != 0xFF)
1058 c = ((unsigned char) c) | ATTR_LINEDRW;
1062 /* If UK-ASCII, make the '#' a LineDraw Pound */
1064 c = '}' | ATTR_LINEDRW;
1067 /*FALLTHROUGH*/ case ATTR_ASCII:
1068 if (unitab_ctrl[c] != 0xFF)
1071 c = ((unsigned char) c) | ATTR_ASCII;
1074 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1080 /* How about C1 controls ? */
1081 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1082 has_compat(VT220)) {
1083 termstate = SEEN_ESC;
1085 c = '@' + (c & 0x1F);
1088 /* Or the GL control. */
1089 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1090 if (curs.x && !wrapnext)
1094 *cpos = (' ' | curr_attr | ATTR_ASCII);
1096 /* Or normal C0 controls. */
1097 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1099 case '\005': /* terminal type query */
1100 /* Strictly speaking this is VT100 but a VT100 defaults to
1101 * no response. Other terminals respond at their option.
1103 * Don't put a CR in the default string as this tends to
1104 * upset some weird software.
1106 * An xterm returns "xterm" (5 characters)
1108 compatibility(ANSIMIN);
1110 char abuf[256], *s, *d;
1112 for (s = cfg.answerback, d = abuf; *s; s++) {
1114 if (*s >= 'a' && *s <= 'z')
1115 *d++ = (*s - ('a' - 1));
1116 else if ((*s >= '@' && *s <= '_') ||
1117 *s == '?' || (*s & 0x80))
1122 } else if (*s == '^') {
1127 lpage_send(CP_ACP, abuf, d - abuf);
1132 struct beeptime *newbeep;
1135 ticks = GetTickCount();
1137 if (!beep_overloaded) {
1138 newbeep = smalloc(sizeof(struct beeptime));
1139 newbeep->ticks = ticks;
1140 newbeep->next = NULL;
1144 beeptail->next = newbeep;
1150 * Throw out any beeps that happened more than
1154 beephead->ticks < ticks - cfg.bellovl_t) {
1155 struct beeptime *tmp = beephead;
1156 beephead = tmp->next;
1163 if (cfg.bellovl && beep_overloaded &&
1164 ticks - lastbeep >= cfg.bellovl_s) {
1166 * If we're currently overloaded and the
1167 * last beep was more than s seconds ago,
1168 * leave overload mode.
1170 beep_overloaded = FALSE;
1171 } else if (cfg.bellovl && !beep_overloaded &&
1172 nbeeps >= cfg.bellovl_n) {
1174 * Now, if we have n or more beeps
1175 * remaining in the queue, go into overload
1178 beep_overloaded = TRUE;
1183 * Perform an actual beep if we're not overloaded.
1185 if (!cfg.bellovl || !beep_overloaded) {
1187 if (cfg.beep == BELL_VISUAL) {
1189 vbell_timeout = ticks + VBELL_TIMEOUT;
1197 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1198 else if (curs.x == 0 && curs.y > 0)
1199 curs.x = cols - 1, curs.y--;
1205 seen_disp_event = TRUE;
1208 compatibility(VT100);
1212 compatibility(VT100);
1217 termstate = VT52_ESC;
1219 compatibility(ANSIMIN);
1220 termstate = SEEN_ESC;
1228 seen_disp_event = TRUE;
1230 logtraffic((unsigned char) c, LGTYP_ASCII);
1233 if (has_compat(SCOANSI)) {
1235 erase_lots(FALSE, FALSE, TRUE);
1238 seen_disp_event = 1;
1242 compatibility(VT100);
1244 if (curs.y == marg_b)
1245 scroll(marg_t, marg_b, 1, TRUE);
1246 else if (curs.y < rows - 1)
1252 seen_disp_event = 1;
1254 logtraffic((unsigned char) c, LGTYP_ASCII);
1258 pos old_curs = curs;
1259 unsigned long *ldata = lineptr(curs.y);
1263 } while (curs.x < cols - 1 && !tabs[curs.x]);
1265 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1266 if (curs.x >= cols / 2)
1267 curs.x = cols / 2 - 1;
1274 check_selection(old_curs, curs);
1276 seen_disp_event = TRUE;
1280 switch (termstate) {
1282 /* Only graphic characters get this far, ctrls are stripped above */
1283 if (wrapnext && wrap) {
1284 cpos[1] |= LATTR_WRAPPED;
1285 if (curs.y == marg_b)
1286 scroll(marg_t, marg_b, 1, TRUE);
1287 else if (curs.y < rows - 1)
1295 if (selstate != NO_SELECTION) {
1296 pos cursplus = curs;
1298 check_selection(curs, cursplus);
1300 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1301 logtraffic((unsigned char) c, LGTYP_ASCII);
1303 extern int wcwidth(wchar_t ucs);
1308 width = wcwidth((wchar_t) c);
1311 if (curs.x + 1 != cols) {
1312 *cpos++ = c | ATTR_WIDE | curr_attr;
1313 *cpos++ = UCSWIDE | curr_attr;
1318 *cpos++ = c | curr_attr;
1325 if (curs.x == cols) {
1329 if (wrap && vt52_mode) {
1330 cpos[1] |= LATTR_WRAPPED;
1331 if (curs.y == marg_b)
1332 scroll(marg_t, marg_b, 1, TRUE);
1333 else if (curs.y < rows - 1)
1340 seen_disp_event = 1;
1345 * This state is virtually identical to SEEN_ESC, with the
1346 * exception that we have an OSC sequence in the pipeline,
1347 * and _if_ we see a backslash, we process it.
1351 termstate = TOPLEVEL;
1354 /* else fall through */
1356 if (c >= ' ' && c <= '/') {
1363 termstate = TOPLEVEL;
1364 switch (ANSI(c, esc_query)) {
1365 case '[': /* enter CSI mode */
1366 termstate = SEEN_CSI;
1368 esc_args[0] = ARG_DEFAULT;
1371 case ']': /* xterm escape sequences */
1372 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1373 compatibility(OTHER);
1374 termstate = SEEN_OSC;
1377 case '7': /* save cursor */
1378 compatibility(VT100);
1381 case '8': /* restore cursor */
1382 compatibility(VT100);
1384 seen_disp_event = TRUE;
1387 compatibility(VT100);
1388 app_keypad_keys = TRUE;
1391 compatibility(VT100);
1392 app_keypad_keys = FALSE;
1394 case 'D': /* exactly equivalent to LF */
1395 compatibility(VT100);
1396 if (curs.y == marg_b)
1397 scroll(marg_t, marg_b, 1, TRUE);
1398 else if (curs.y < rows - 1)
1402 seen_disp_event = TRUE;
1404 case 'E': /* exactly equivalent to CR-LF */
1405 compatibility(VT100);
1407 if (curs.y == marg_b)
1408 scroll(marg_t, marg_b, 1, TRUE);
1409 else if (curs.y < rows - 1)
1413 seen_disp_event = TRUE;
1415 case 'M': /* reverse index - backwards LF */
1416 compatibility(VT100);
1417 if (curs.y == marg_t)
1418 scroll(marg_t, marg_b, -1, TRUE);
1419 else if (curs.y > 0)
1423 seen_disp_event = TRUE;
1425 case 'Z': /* terminal type query */
1426 compatibility(VT100);
1427 ldisc_send(id_string, strlen(id_string));
1429 case 'c': /* restore power-on settings */
1430 compatibility(VT100);
1433 request_resize(80, rows, 1);
1438 seen_disp_event = TRUE;
1440 case 'H': /* set a tab */
1441 compatibility(VT100);
1442 tabs[curs.x] = TRUE;
1445 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1446 compatibility(VT100);
1448 unsigned long *ldata;
1452 for (i = 0; i < rows; i++) {
1454 for (j = 0; j < cols; j++)
1455 ldata[j] = ATTR_DEFAULT | 'E';
1459 seen_disp_event = TRUE;
1460 scrtop.x = scrtop.y = 0;
1463 check_selection(scrtop, scrbot);
1467 case ANSI('3', '#'):
1468 case ANSI('4', '#'):
1469 case ANSI('5', '#'):
1470 case ANSI('6', '#'):
1471 compatibility(VT100);
1473 unsigned long nlattr;
1474 unsigned long *ldata;
1475 switch (ANSI(c, esc_query)) {
1476 case ANSI('3', '#'):
1479 case ANSI('4', '#'):
1482 case ANSI('5', '#'):
1483 nlattr = LATTR_NORM;
1485 default: /* spiritually case ANSI('6', '#'): */
1486 nlattr = LATTR_WIDE;
1489 ldata = lineptr(curs.y);
1490 ldata[cols] &= ~LATTR_MODE;
1491 ldata[cols] |= nlattr;
1495 case ANSI('A', '('):
1496 compatibility(VT100);
1497 cset_attr[0] = ATTR_GBCHR;
1499 case ANSI('B', '('):
1500 compatibility(VT100);
1501 cset_attr[0] = ATTR_ASCII;
1503 case ANSI('0', '('):
1504 compatibility(VT100);
1505 cset_attr[0] = ATTR_LINEDRW;
1507 case ANSI('U', '('):
1508 compatibility(OTHER);
1509 cset_attr[0] = ATTR_SCOACS;
1512 case ANSI('A', ')'):
1513 compatibility(VT100);
1514 cset_attr[1] = ATTR_GBCHR;
1516 case ANSI('B', ')'):
1517 compatibility(VT100);
1518 cset_attr[1] = ATTR_ASCII;
1520 case ANSI('0', ')'):
1521 compatibility(VT100);
1522 cset_attr[1] = ATTR_LINEDRW;
1524 case ANSI('U', ')'):
1525 compatibility(OTHER);
1526 cset_attr[1] = ATTR_SCOACS;
1529 case ANSI('8', '%'): /* Old Linux code */
1530 case ANSI('G', '%'):
1531 compatibility(OTHER);
1534 case ANSI('@', '%'):
1535 compatibility(OTHER);
1541 termstate = TOPLEVEL; /* default */
1543 if (esc_nargs <= ARGS_MAX) {
1544 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1545 esc_args[esc_nargs - 1] = 0;
1546 esc_args[esc_nargs - 1] =
1547 10 * esc_args[esc_nargs - 1] + c - '0';
1549 termstate = SEEN_CSI;
1550 } else if (c == ';') {
1551 if (++esc_nargs <= ARGS_MAX)
1552 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1553 termstate = SEEN_CSI;
1554 } else if (c < '@') {
1561 termstate = SEEN_CSI;
1563 switch (ANSI(c, esc_query)) {
1564 case 'A': /* move up N lines */
1565 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1566 seen_disp_event = TRUE;
1568 case 'e': /* move down N lines */
1569 compatibility(ANSI);
1572 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1573 seen_disp_event = TRUE;
1575 case ANSI('c', '>'): /* report xterm version */
1576 compatibility(OTHER);
1577 /* this reports xterm version 136 so that VIM can
1578 use the drag messages from the mouse reporting */
1579 ldisc_send("\033[>0;136;0c", 11);
1581 case 'a': /* move right N cols */
1582 compatibility(ANSI);
1585 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1586 seen_disp_event = TRUE;
1588 case 'D': /* move left N cols */
1589 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1590 seen_disp_event = TRUE;
1592 case 'E': /* move down N lines and CR */
1593 compatibility(ANSI);
1594 move(0, curs.y + def(esc_args[0], 1), 1);
1595 seen_disp_event = TRUE;
1597 case 'F': /* move up N lines and CR */
1598 compatibility(ANSI);
1599 move(0, curs.y - def(esc_args[0], 1), 1);
1600 seen_disp_event = TRUE;
1603 case '`': /* set horizontal posn */
1604 compatibility(ANSI);
1605 move(def(esc_args[0], 1) - 1, curs.y, 0);
1606 seen_disp_event = TRUE;
1608 case 'd': /* set vertical posn */
1609 compatibility(ANSI);
1611 (dec_om ? marg_t : 0) + def(esc_args[0],
1614 seen_disp_event = TRUE;
1617 case 'f': /* set horz and vert posns at once */
1619 esc_args[1] = ARG_DEFAULT;
1620 move(def(esc_args[1], 1) - 1,
1621 (dec_om ? marg_t : 0) + def(esc_args[0],
1624 seen_disp_event = TRUE;
1626 case 'J': /* erase screen or parts of it */
1628 unsigned int i = def(esc_args[0], 0) + 1;
1631 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1634 seen_disp_event = TRUE;
1636 case 'K': /* erase line or parts of it */
1638 unsigned int i = def(esc_args[0], 0) + 1;
1641 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1643 seen_disp_event = TRUE;
1645 case 'L': /* insert lines */
1646 compatibility(VT102);
1647 if (curs.y <= marg_b)
1648 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1651 seen_disp_event = TRUE;
1653 case 'M': /* delete lines */
1654 compatibility(VT102);
1655 if (curs.y <= marg_b)
1656 scroll(curs.y, marg_b, def(esc_args[0], 1),
1659 seen_disp_event = TRUE;
1661 case '@': /* insert chars */
1662 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1663 compatibility(VT102);
1664 insch(def(esc_args[0], 1));
1665 seen_disp_event = TRUE;
1667 case 'P': /* delete chars */
1668 compatibility(VT102);
1669 insch(-def(esc_args[0], 1));
1670 seen_disp_event = TRUE;
1672 case 'c': /* terminal type query */
1673 compatibility(VT100);
1674 /* This is the response for a VT102 */
1675 ldisc_send(id_string, strlen(id_string));
1677 case 'n': /* cursor position query */
1678 if (esc_args[0] == 6) {
1680 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1682 ldisc_send(buf, strlen(buf));
1683 } else if (esc_args[0] == 5) {
1684 ldisc_send("\033[0n", 4);
1687 case 'h': /* toggle modes to high */
1689 compatibility(VT100);
1692 for (i = 0; i < esc_nargs; i++)
1693 toggle_mode(esc_args[i], esc_query, TRUE);
1696 case 'l': /* toggle modes to low */
1698 compatibility(VT100);
1701 for (i = 0; i < esc_nargs; i++)
1702 toggle_mode(esc_args[i], esc_query, FALSE);
1705 case 'g': /* clear tabs */
1706 compatibility(VT100);
1707 if (esc_nargs == 1) {
1708 if (esc_args[0] == 0) {
1709 tabs[curs.x] = FALSE;
1710 } else if (esc_args[0] == 3) {
1712 for (i = 0; i < cols; i++)
1717 case 'r': /* set scroll margins */
1718 compatibility(VT100);
1719 if (esc_nargs <= 2) {
1721 top = def(esc_args[0], 1) - 1;
1722 bot = (esc_nargs <= 1
1724 0 ? rows : def(esc_args[1], rows)) - 1;
1727 /* VTTEST Bug 9 - if region is less than 2 lines
1728 * don't change region.
1730 if (bot - top > 0) {
1735 * I used to think the cursor should be
1736 * placed at the top of the newly marginned
1737 * area. Apparently not: VMS TPU falls over
1740 * Well actually it should for Origin mode - RDB
1742 curs.y = (dec_om ? marg_t : 0);
1744 seen_disp_event = TRUE;
1748 case 'm': /* set graphics rendition */
1751 * A VT100 without the AVO only had one attribute, either
1752 * underline or reverse video depending on the cursor type,
1753 * this was selected by CSI 7m.
1756 * This is sometimes DIM, eg on the GIGI and Linux
1758 * This is sometimes INVIS various ANSI.
1760 * This like 22 disables BOLD, DIM and INVIS
1762 * The ANSI colours appear on any terminal that has colour
1763 * (obviously) but the interaction between sgr0 and the
1764 * colours varies but is usually related to the background
1765 * colour erase item.
1766 * The interaction between colour attributes and the mono
1767 * ones is also very implementation dependent.
1769 * The 39 and 49 attributes are likely to be unimplemented.
1772 for (i = 0; i < esc_nargs; i++) {
1773 switch (def(esc_args[i], 0)) {
1774 case 0: /* restore defaults */
1775 curr_attr = ATTR_DEFAULT;
1777 case 1: /* enable bold */
1778 compatibility(VT100AVO);
1779 curr_attr |= ATTR_BOLD;
1781 case 21: /* (enable double underline) */
1782 compatibility(OTHER);
1783 case 4: /* enable underline */
1784 compatibility(VT100AVO);
1785 curr_attr |= ATTR_UNDER;
1787 case 5: /* enable blink */
1788 compatibility(VT100AVO);
1789 curr_attr |= ATTR_BLINK;
1791 case 7: /* enable reverse video */
1792 curr_attr |= ATTR_REVERSE;
1794 case 10: /* SCO acs off */
1795 compatibility(SCOANSI);
1797 case 11: /* SCO acs on */
1798 compatibility(SCOANSI);
1800 case 12: /* SCO acs on flipped */
1801 compatibility(SCOANSI);
1803 case 22: /* disable bold */
1804 compatibility2(OTHER, VT220);
1805 curr_attr &= ~ATTR_BOLD;
1807 case 24: /* disable underline */
1808 compatibility2(OTHER, VT220);
1809 curr_attr &= ~ATTR_UNDER;
1811 case 25: /* disable blink */
1812 compatibility2(OTHER, VT220);
1813 curr_attr &= ~ATTR_BLINK;
1815 case 27: /* disable reverse video */
1816 compatibility2(OTHER, VT220);
1817 curr_attr &= ~ATTR_REVERSE;
1828 curr_attr &= ~ATTR_FGMASK;
1830 (esc_args[i] - 30) << ATTR_FGSHIFT;
1832 case 39: /* default-foreground */
1833 curr_attr &= ~ATTR_FGMASK;
1834 curr_attr |= ATTR_DEFFG;
1845 curr_attr &= ~ATTR_BGMASK;
1847 (esc_args[i] - 40) << ATTR_BGSHIFT;
1849 case 49: /* default-background */
1850 curr_attr &= ~ATTR_BGMASK;
1851 curr_attr |= ATTR_DEFBG;
1856 erase_char = (' ' | ATTR_ASCII |
1858 (ATTR_FGMASK | ATTR_BGMASK)));
1861 case 's': /* save cursor */
1864 case 'u': /* restore cursor */
1866 seen_disp_event = TRUE;
1868 case 't': /* set page size - ie window height */
1870 * VT340/VT420 sequence DECSLPP, DEC only allows values
1871 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1872 * illegal values (eg first arg 1..9) for window changing
1875 compatibility(VT340TEXT);
1877 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1878 request_resize(cols, def(esc_args[0], 24), 0);
1883 compatibility(SCOANSI);
1884 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1887 seen_disp_event = TRUE;
1890 compatibility(SCOANSI);
1891 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1894 seen_disp_event = TRUE;
1896 case ANSI('|', '*'):
1897 /* VT420 sequence DECSNLS
1898 * Set number of lines on screen
1899 * VT420 uses VGA like hardware and can support any size in
1900 * reasonable range (24..49 AIUI) with no default specified.
1902 compatibility(VT420);
1903 if (esc_nargs == 1 && esc_args[0] > 0) {
1904 request_resize(cols,
1905 def(esc_args[0], cfg.height),
1910 case ANSI('|', '$'):
1911 /* VT340/VT420 sequence DECSCPP
1912 * Set number of columns per page
1913 * Docs imply range is only 80 or 132, but I'll allow any.
1915 compatibility(VT340TEXT);
1916 if (esc_nargs <= 1) {
1917 request_resize(def(esc_args[0], cfg.width),
1922 case 'X': /* write N spaces w/o moving cursor */
1923 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1924 compatibility(ANSIMIN);
1926 int n = def(esc_args[0], 1);
1928 unsigned long *p = cpos;
1929 if (n > cols - curs.x)
1933 check_selection(curs, cursplus);
1936 seen_disp_event = TRUE;
1939 case 'x': /* report terminal characteristics */
1940 compatibility(VT100);
1943 int i = def(esc_args[0], 0);
1944 if (i == 0 || i == 1) {
1945 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1947 ldisc_send(buf, 20);
1951 case ANSI('L', '='):
1952 compatibility(OTHER);
1953 use_bce = (esc_args[0] <= 0);
1954 erase_char = ERASE_CHAR;
1956 erase_char = (' ' | ATTR_ASCII |
1958 (ATTR_FGMASK | ATTR_BGMASK)));
1960 case ANSI('E', '='):
1961 compatibility(OTHER);
1962 blink_is_real = (esc_args[0] >= 1);
1964 case ANSI('p', '"'):
1965 /* Allow the host to make this emulator a 'perfect' VT102.
1966 * This first appeared in the VT220, but we do need to get
1967 * back to PuTTY mode so I won't check it.
1969 * The arg in 40..42,50 are a PuTTY extension.
1970 * The 2nd arg, 8bit vs 7bit is not checked.
1972 * Setting VT102 mode should also change the Fkeys to
1973 * generate PF* codes as a real VT102 has no Fkeys.
1974 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1977 * Note ESC c will NOT change this!
1980 switch (esc_args[0]) {
1982 compatibility_level &= ~TM_VTXXX;
1983 compatibility_level |= TM_VT102;
1986 compatibility_level &= ~TM_VTXXX;
1987 compatibility_level |= TM_VT220;
1991 if (esc_args[0] > 60 && esc_args[0] < 70)
1992 compatibility_level |= TM_VTXXX;
1996 compatibility_level &= TM_VTXXX;
1999 compatibility_level = TM_PUTTY;
2002 compatibility_level = TM_SCOANSI;
2006 compatibility_level = TM_PUTTY;
2012 /* Change the response to CSI c */
2013 if (esc_args[0] == 50) {
2016 strcpy(id_string, "\033[?");
2017 for (i = 1; i < esc_nargs; i++) {
2019 strcat(id_string, ";");
2020 sprintf(lbuf, "%d", esc_args[i]);
2021 strcat(id_string, lbuf);
2023 strcat(id_string, "c");
2026 /* Is this a good idea ?
2027 * Well we should do a soft reset at this point ...
2029 if (!has_compat(VT420) && has_compat(VT100)) {
2031 request_resize(132, 24, 1);
2033 request_resize(80, 24, 1);
2042 case 'P': /* Linux palette sequence */
2043 termstate = SEEN_OSC_P;
2046 case 'R': /* Linux palette reset */
2049 termstate = TOPLEVEL;
2051 case 'W': /* word-set */
2052 termstate = SEEN_OSC_W;
2065 esc_args[0] = 10 * esc_args[0] + c - '0';
2069 * Grotty hack to support xterm and DECterm title
2070 * sequences concurrently.
2072 if (esc_args[0] == 2) {
2076 /* else fall through */
2078 termstate = OSC_STRING;
2084 * This OSC stuff is EVIL. It takes just one character to get into
2085 * sysline mode and it's not initially obvious how to get out.
2086 * So I've added CR and LF as string aborts.
2087 * This shouldn't effect compatibility as I believe embedded
2088 * control characters are supposed to be interpreted (maybe?)
2089 * and they don't display anything useful anyway.
2093 if (c == '\n' || c == '\r') {
2094 termstate = TOPLEVEL;
2095 } else if (c == 0234 || c == '\007') {
2097 * These characters terminate the string; ST and BEL
2098 * terminate the sequence and trigger instant
2099 * processing of it, whereas ESC goes back to SEEN_ESC
2100 * mode unless it is followed by \, in which case it is
2101 * synonymous with ST in the first place.
2104 termstate = TOPLEVEL;
2105 } else if (c == '\033')
2106 termstate = OSC_MAYBE_ST;
2107 else if (osc_strlen < OSC_STR_MAX)
2108 osc_string[osc_strlen++] = c;
2112 int max = (osc_strlen == 0 ? 21 : 16);
2114 if (c >= '0' && c <= '9')
2116 else if (c >= 'A' && c <= 'A' + max - 10)
2118 else if (c >= 'a' && c <= 'a' + max - 10)
2121 termstate = TOPLEVEL;
2124 osc_string[osc_strlen++] = val;
2125 if (osc_strlen >= 7) {
2126 palette_set(osc_string[0],
2127 osc_string[1] * 16 + osc_string[2],
2128 osc_string[3] * 16 + osc_string[4],
2129 osc_string[5] * 16 + osc_string[6]);
2131 termstate = TOPLEVEL;
2147 esc_args[0] = 10 * esc_args[0] + c - '0';
2150 termstate = OSC_STRING;
2155 termstate = TOPLEVEL;
2156 seen_disp_event = TRUE;
2159 move(curs.x, curs.y - 1, 1);
2162 move(curs.x, curs.y + 1, 1);
2165 move(curs.x + 1, curs.y, 1);
2168 move(curs.x - 1, curs.y, 1);
2171 * From the VT100 Manual
2172 * NOTE: The special graphics characters in the VT100
2173 * are different from those in the VT52
2175 * From VT102 manual:
2176 * 137 _ Blank - Same
2177 * 140 ` Reserved - Humm.
2178 * 141 a Solid rectangle - Similar
2179 * 142 b 1/ - Top half of fraction for the
2180 * 143 c 3/ - subscript numbers below.
2183 * 146 f Degrees - Same
2184 * 147 g Plus or minus - Same
2186 * 151 i Ellipsis (dots)
2189 * 154 l Bar at scan 0
2190 * 155 m Bar at scan 1
2191 * 156 n Bar at scan 2
2192 * 157 o Bar at scan 3 - Similar
2193 * 160 p Bar at scan 4 - Similar
2194 * 161 q Bar at scan 5 - Similar
2195 * 162 r Bar at scan 6 - Same
2196 * 163 s Bar at scan 7 - Similar
2211 cset_attr[cset = 0] = ATTR_LINEDRW;
2214 cset_attr[cset = 0] = ATTR_ASCII;
2221 scroll(0, rows - 1, -1, TRUE);
2222 else if (curs.y > 0)
2228 erase_lots(FALSE, FALSE, TRUE);
2232 erase_lots(TRUE, FALSE, TRUE);
2236 /* XXX Print cursor line */
2239 /* XXX Start controller mode */
2242 /* XXX Stop controller mode */
2246 termstate = VT52_Y1;
2249 ldisc_send("\033/Z", 3);
2252 app_keypad_keys = TRUE;
2255 app_keypad_keys = FALSE;
2258 /* XXX This should switch to VT100 mode not current or default
2259 * VT mode. But this will only have effect in a VT220+
2263 blink_is_real = cfg.blinktext;
2267 /* XXX Enter auto print mode */
2270 /* XXX Exit auto print mode */
2273 /* XXX Print screen */
2279 /* compatibility(ATARI) */
2281 erase_lots(FALSE, FALSE, TRUE);
2285 /* compatibility(ATARI) */
2286 if (curs.y <= marg_b)
2287 scroll(curs.y, marg_b, -1, FALSE);
2290 /* compatibility(ATARI) */
2291 if (curs.y <= marg_b)
2292 scroll(curs.y, marg_b, 1, TRUE);
2295 /* compatibility(ATARI) */
2296 termstate = VT52_FG;
2299 /* compatibility(ATARI) */
2300 termstate = VT52_BG;
2303 /* compatibility(ATARI) */
2304 erase_lots(FALSE, TRUE, FALSE);
2308 /* compatibility(ATARI) */
2312 /* compatibility(ATARI) */
2315 /* case 'j': Save cursor position - broken on ST */
2316 /* case 'k': Restore cursor position */
2318 /* compatibility(ATARI) */
2319 erase_lots(TRUE, TRUE, TRUE);
2325 /* compatibility(ATARI) */
2326 erase_lots(TRUE, TRUE, FALSE);
2329 /* compatibility(ATARI) */
2330 curr_attr |= ATTR_REVERSE;
2333 /* compatibility(ATARI) */
2334 curr_attr &= ~ATTR_REVERSE;
2336 case 'v': /* wrap Autowrap on - Wyse style */
2337 /* compatibility(ATARI) */
2340 case 'w': /* Autowrap off */
2341 /* compatibility(ATARI) */
2346 /* compatibility(OTHER) */
2348 curr_attr = ATTR_DEFAULT;
2350 erase_char = (' ' | ATTR_ASCII |
2352 (ATTR_FGMASK | ATTR_BGMASK)));
2355 /* compatibility(VI50) */
2356 curr_attr |= ATTR_UNDER;
2359 /* compatibility(VI50) */
2360 curr_attr &= ~ATTR_UNDER;
2363 /* compatibility(VI50) */
2365 curr_attr |= ATTR_BOLD;
2368 /* compatibility(VI50) */
2370 curr_attr &= ~ATTR_BOLD;
2376 termstate = VT52_Y2;
2377 move(curs.x, c - ' ', 0);
2380 termstate = TOPLEVEL;
2381 move(c - ' ', curs.y, 0);
2386 termstate = TOPLEVEL;
2387 curr_attr &= ~ATTR_FGMASK;
2388 curr_attr &= ~ATTR_BOLD;
2389 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2390 if ((c & 0x8) || vt52_bold)
2391 curr_attr |= ATTR_BOLD;
2394 erase_char = (' ' | ATTR_ASCII |
2395 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2398 termstate = TOPLEVEL;
2399 curr_attr &= ~ATTR_BGMASK;
2400 curr_attr &= ~ATTR_BLINK;
2401 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2403 /* Note: bold background */
2405 curr_attr |= ATTR_BLINK;
2408 erase_char = (' ' | ATTR_ASCII |
2409 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2412 default: break; /* placate gcc warning about enum use */
2414 if (selstate != NO_SELECTION) {
2415 pos cursplus = curs;
2417 check_selection(curs, cursplus);
2425 * Compare two lines to determine whether they are sufficiently
2426 * alike to scroll-optimise one to the other. Return the degree of
2429 static int linecmp(unsigned long *a, unsigned long *b)
2433 for (i = n = 0; i < cols; i++)
2434 n += (*a++ == *b++);
2440 * Given a context, update the window. Out of paranoia, we don't
2441 * allow WM_PAINT responses to do scrolling optimisations.
2443 static void do_paint(Context ctx, int may_optimise)
2445 int i, j, our_curs_y;
2446 unsigned long rv, cursor;
2449 long cursor_background = ERASE_CHAR;
2453 * Check the visual bell state.
2456 ticks = GetTickCount();
2457 if (ticks - vbell_timeout >= 0)
2461 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2464 * screen array, disptop, scrtop,
2466 * cfg.blinkpc, blink_is_real, tblinker,
2467 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2470 /* Has the cursor position or type changed ? */
2473 if (blinker || !cfg.blink_cur)
2474 cursor = TATTR_ACTCURS;
2478 cursor = TATTR_PASCURS;
2480 cursor |= TATTR_RIGHTCURS;
2483 our_curs_y = curs.y - disptop;
2485 if (dispcurs && (curstype != cursor ||
2487 disptext + our_curs_y * (cols + 1) + curs.x)) {
2488 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2489 dispcurs[-1] |= ATTR_INVALID;
2490 if ((*dispcurs & ATTR_WIDE))
2491 dispcurs[1] |= ATTR_INVALID;
2492 *dispcurs |= ATTR_INVALID;
2497 /* The normal screen data */
2498 for (i = 0; i < rows; i++) {
2499 unsigned long *ldata;
2501 int idx, dirty_line, dirty_run;
2502 unsigned long attr = 0;
2503 int updated_line = 0;
2506 int last_run_dirty = 0;
2508 scrpos.y = i + disptop;
2509 ldata = lineptr(scrpos.y);
2510 lattr = (ldata[cols] & LATTR_MODE);
2512 idx = i * (cols + 1);
2513 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2514 disptext[idx + cols] = ldata[cols];
2516 for (j = 0; j < cols; j++, idx++) {
2517 unsigned long tattr, tchar;
2518 unsigned long *d = ldata + j;
2522 tchar = (*d & (CHAR_MASK | CSET_MASK));
2523 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2524 switch (tchar & CSET_MASK) {
2526 tchar = unitab_line[tchar & 0xFF];
2529 tchar = unitab_xterm[tchar & 0xFF];
2532 tchar = unitab_scoacs[tchar&0xFF];
2535 tattr |= (tchar & CSET_MASK);
2538 /* Video reversing things */
2540 ^ (posle(selstart, scrpos) &&
2541 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2543 /* 'Real' blinking ? */
2544 if (blink_is_real && (tattr & ATTR_BLINK)) {
2545 if (has_focus && tblinker) {
2547 tattr &= ~CSET_MASK;
2550 tattr &= ~ATTR_BLINK;
2553 /* Cursor here ? Save the 'background' */
2554 if (i == our_curs_y && j == curs.x) {
2555 cursor_background = tattr | tchar;
2556 dispcurs = disptext + idx;
2559 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2562 break_run = (tattr != attr || j - start >= sizeof(ch));
2564 /* Special hack for VT100 Linedraw glyphs */
2565 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2566 && tchar <= 0xBD) break_run = TRUE;
2568 if (!dbcs_screenfont && !dirty_line) {
2569 if ((tchar | tattr) == disptext[idx])
2571 else if (!dirty_run && ccount == 1)
2576 if ((dirty_run || last_run_dirty) && ccount > 0) {
2577 do_text(ctx, start, i, ch, ccount, attr, lattr);
2583 if (dbcs_screenfont)
2584 last_run_dirty = dirty_run;
2585 dirty_run = dirty_line;
2588 if ((tchar | tattr) != disptext[idx])
2590 ch[ccount++] = (char) tchar;
2591 disptext[idx] = tchar | tattr;
2593 /* If it's a wide char step along to the next one. */
2594 if (tattr & ATTR_WIDE) {
2598 /* Cursor is here ? Ouch! */
2599 if (i == our_curs_y && j == curs.x) {
2600 cursor_background = *d;
2601 dispcurs = disptext + idx;
2603 if (disptext[idx] != *d)
2609 if (dirty_run && ccount > 0) {
2610 do_text(ctx, start, i, ch, ccount, attr, lattr);
2614 /* Cursor on this line ? (and changed) */
2615 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2616 ch[0] = (char) (cursor_background & CHAR_MASK);
2617 attr = (cursor_background & ATTR_MASK) | cursor;
2618 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2625 * Flick the switch that says if blinking things should be shown or hidden.
2628 void term_blink(int flg)
2630 static long last_blink = 0;
2631 static long last_tblink = 0;
2632 long now, blink_diff;
2634 now = GetTickCount();
2635 blink_diff = now - last_tblink;
2637 /* Make sure the text blinks no more than 2Hz */
2638 if (blink_diff < 0 || blink_diff > 450) {
2640 tblinker = !tblinker;
2649 blink_diff = now - last_blink;
2651 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2652 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2660 * Invalidate the whole screen so it will be repainted in full.
2662 void term_invalidate(void)
2666 for (i = 0; i < rows * (cols + 1); i++)
2667 disptext[i] = ATTR_INVALID;
2671 * Paint the window in response to a WM_PAINT message.
2673 void term_paint(Context ctx, int l, int t, int r, int b)
2675 int i, j, left, top, right, bottom;
2677 left = l / font_width;
2678 right = (r - 1) / font_width;
2679 top = t / font_height;
2680 bottom = (b - 1) / font_height;
2681 for (i = top; i <= bottom && i < rows; i++) {
2682 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2683 for (j = left; j <= right && j < cols; j++)
2684 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2686 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2687 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2690 /* This should happen soon enough, also for some reason it sometimes
2691 * fails to actually do anything when re-sizing ... painting the wrong
2693 do_paint (ctx, FALSE);
2698 * Attempt to scroll the scrollback. The second parameter gives the
2699 * position we want to scroll to; the first is +1 to denote that
2700 * this position is relative to the beginning of the scrollback, -1
2701 * to denote it is relative to the end, and 0 to denote that it is
2702 * relative to the current position.
2704 void term_scroll(int rel, int where)
2706 int sbtop = -count234(scrollback);
2708 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2709 if (disptop < sbtop)
2717 static void clipme(pos top, pos bottom)
2720 wchar_t *wbptr; /* where next char goes within workbuf */
2721 int wblen = 0; /* workbuf len */
2722 int buflen; /* amount of memory allocated to workbuf */
2724 buflen = 5120; /* Default size */
2725 workbuf = smalloc(buflen * sizeof(wchar_t));
2726 wbptr = workbuf; /* start filling here */
2728 while (poslt(top, bottom)) {
2730 unsigned long *ldata = lineptr(top.y);
2736 if (!(ldata[cols] & LATTR_WRAPPED)) {
2737 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2738 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2739 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2740 && poslt(top, nlpos))
2742 if (poslt(nlpos, bottom))
2745 while (poslt(top, bottom) && poslt(top, nlpos)) {
2748 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2750 wchar_t cbuf[16], *p;
2751 int uc = (ldata[top.x] & 0xFFFF);
2754 if (uc == UCSWIDE) {
2759 switch (uc & CSET_MASK) {
2762 uc = unitab_xterm[uc & 0xFF];
2766 uc = unitab_line[uc & 0xFF];
2769 uc = unitab_scoacs[uc&0xFF];
2772 switch (uc & CSET_MASK) {
2774 uc = unitab_font[uc & 0xFF];
2777 uc = unitab_oemcp[uc & 0xFF];
2781 set = (uc & CSET_MASK);
2782 c = (uc & CHAR_MASK);
2786 if (DIRECT_FONT(uc)) {
2787 if (c >= ' ' && c != 0x7F) {
2788 unsigned char buf[4];
2791 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2793 buf[1] = (unsigned char) ldata[top.x + 1];
2794 rv = MultiByteToWideChar(font_codepage,
2795 0, buf, 2, wbuf, 4);
2799 rv = MultiByteToWideChar(font_codepage,
2800 0, buf, 1, wbuf, 4);
2804 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2811 for (p = cbuf; *p; p++) {
2812 /* Enough overhead for trailing NL and nul */
2813 if (wblen >= buflen - 16) {
2816 sizeof(wchar_t) * (buflen += 100));
2817 wbptr = workbuf + wblen;
2826 for (i = 0; i < sel_nl_sz; i++) {
2828 *wbptr++ = sel_nl[i];
2836 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2837 if (buflen > 0) /* indicates we allocated this buffer */
2841 void term_copyall(void)
2844 top.y = -count234(scrollback);
2850 * The wordness array is mainly for deciding the disposition of the US-ASCII
2853 static int wordtype(int uc)
2856 int start, end, ctype;
2857 } *wptr, ucs_words[] = {
2863 0x037e, 0x037e, 1}, /* Greek question mark */
2865 0x0387, 0x0387, 1}, /* Greek ano teleia */
2867 0x055a, 0x055f, 1}, /* Armenian punctuation */
2869 0x0589, 0x0589, 1}, /* Armenian full stop */
2871 0x0700, 0x070d, 1}, /* Syriac punctuation */
2873 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2875 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2877 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2879 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2881 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2883 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2885 0x2000, 0x200a, 0}, /* Various spaces */
2887 0x2070, 0x207f, 2}, /* superscript */
2889 0x2080, 0x208f, 2}, /* subscript */
2891 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2893 0x3000, 0x3000, 0}, /* ideographic space */
2895 0x3001, 0x3020, 1}, /* ideographic punctuation */
2897 0x303f, 0x309f, 3}, /* Hiragana */
2899 0x30a0, 0x30ff, 3}, /* Katakana */
2901 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2903 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2905 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2907 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2909 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2911 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2913 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2915 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2917 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2922 uc &= (CSET_MASK | CHAR_MASK);
2924 switch (uc & CSET_MASK) {
2926 uc = unitab_xterm[uc & 0xFF];
2929 uc = unitab_line[uc & 0xFF];
2932 uc = unitab_scoacs[uc&0xFF];
2935 switch (uc & CSET_MASK) {
2937 uc = unitab_font[uc & 0xFF];
2940 uc = unitab_oemcp[uc & 0xFF];
2945 return wordness[uc];
2947 for (wptr = ucs_words; wptr->start; wptr++) {
2948 if (uc >= wptr->start && uc <= wptr->end)
2956 * Spread the selection outwards according to the selection mode.
2958 static pos sel_spread_half(pos p, int dir)
2960 unsigned long *ldata;
2963 ldata = lineptr(p.y);
2968 * In this mode, every character is a separate unit, except
2969 * for runs of spaces at the end of a non-wrapping line.
2971 if (!(ldata[cols] & LATTR_WRAPPED)) {
2972 unsigned long *q = ldata + cols;
2973 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2975 if (q == ldata + cols)
2977 if (p.x >= q - ldata)
2978 p.x = (dir == -1 ? q - ldata : cols - 1);
2983 * In this mode, the units are maximal runs of characters
2984 * whose `wordness' has the same value.
2986 wvalue = wordtype(ldata[p.x]);
2988 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2991 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2997 * In this mode, every line is a unit.
2999 p.x = (dir == -1 ? 0 : cols - 1);
3005 static void sel_spread(void)
3007 selstart = sel_spread_half(selstart, -1);
3009 selend = sel_spread_half(selend, +1);
3013 void term_do_paste(void)
3018 get_clip(&data, &len);
3023 sfree(paste_buffer);
3024 paste_pos = paste_hold = paste_len = 0;
3025 paste_buffer = smalloc(len * sizeof(wchar_t));
3028 while (p < data + len) {
3029 while (p < data + len &&
3030 !(p <= data + len - sel_nl_sz &&
3031 !memcmp(p, sel_nl, sizeof(sel_nl))))
3036 for (i = 0; i < p - q; i++) {
3037 paste_buffer[paste_len++] = q[i];
3041 if (p <= data + len - sel_nl_sz &&
3042 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3043 paste_buffer[paste_len++] = '\r';
3049 /* Assume a small paste will be OK in one go. */
3050 if (paste_len < 256) {
3051 luni_send(paste_buffer, paste_len);
3053 sfree(paste_buffer);
3055 paste_pos = paste_hold = paste_len = 0;
3058 get_clip(NULL, NULL);
3061 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3062 int shift, int ctrl)
3065 unsigned long *ldata;
3081 selpoint.y = y + disptop;
3083 ldata = lineptr(selpoint.y);
3084 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3088 int encstate = 0, r, c;
3090 static int is_down = 0;
3094 encstate = 0x20; /* left button down */
3105 case MBT_WHEEL_DOWN:
3108 default: break; /* placate gcc warning about enum use */
3112 if (xterm_mouse == 1)
3125 default: break; /* placate gcc warning about enum use */
3134 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3135 ldisc_send(abuf, 6);
3139 b = translate_button(b);
3141 if (b == MBT_SELECT && a == MA_CLICK) {
3143 selstate = ABOUT_TO;
3144 selanchor = selpoint;
3146 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3148 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3149 selstate = DRAGGING;
3150 selstart = selanchor = selpoint;
3154 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3155 (b == MBT_EXTEND && a != MA_RELEASE)) {
3156 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3158 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3159 if (posdiff(selpoint, selstart) <
3160 posdiff(selend, selstart) / 2) {
3164 selanchor = selstart;
3166 selstate = DRAGGING;
3168 if (selstate != ABOUT_TO && selstate != DRAGGING)
3169 selanchor = selpoint;
3170 selstate = DRAGGING;
3171 if (poslt(selpoint, selanchor)) {
3172 selstart = selpoint;
3176 selstart = selanchor;
3181 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3182 if (selstate == DRAGGING) {
3184 * We've completed a selection. We now transfer the
3185 * data to the clipboard.
3187 clipme(selstart, selend);
3188 selstate = SELECTED;
3190 selstate = NO_SELECTION;
3191 } else if (b == MBT_PASTE
3192 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3203 sfree(paste_buffer);
3210 static long last_paste = 0;
3211 long now, paste_diff;
3216 /* Don't wait forever to paste */
3218 now = GetTickCount();
3219 paste_diff = now - last_paste;
3220 if (paste_diff >= 0 && paste_diff < 450)
3225 while (paste_pos < paste_len) {
3227 while (n + paste_pos < paste_len) {
3228 if (paste_buffer[paste_pos + n++] == '\r')
3231 luni_send(paste_buffer + paste_pos, n);
3234 if (paste_pos < paste_len) {
3239 sfree(paste_buffer);
3244 static void deselect(void)
3246 selstate = NO_SELECTION;
3247 selstart.x = selstart.y = selend.x = selend.y = 0;
3250 void term_deselect(void)
3256 int term_ldisc(int option)
3258 if (option == LD_ECHO)
3259 return term_echoing;
3260 if (option == LD_EDIT)
3261 return term_editing;
3266 * from_backend(), to get data from the backend for the terminal.
3268 void from_backend(int is_stderr, char *data, int len)
3271 if (inbuf_head >= INBUF_SIZE)
3273 inbuf[inbuf_head++] = *data++;
3278 * Log session traffic.
3280 void logtraffic(unsigned char c, int logmode)
3282 if (cfg.logtype > 0) {
3283 if (cfg.logtype == logmode) {
3284 /* deferred open file from pgm start? */
3293 /* open log file append/overwrite mode */
3303 sprintf(writemod, "wb"); /* default to rewrite */
3304 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3308 i = askappend(cfg.logfilename);
3310 writemod[0] = 'a'; /* set append mode */
3311 else if (i == 0) { /* cancelled */
3313 cfg.logtype = 0; /* disable logging */
3318 lgfp = fopen(cfg.logfilename, writemod);
3319 if (lgfp) { /* enter into event log */
3320 sprintf(buf, "%s session log (%s mode) to file : ",
3321 (writemod[0] == 'a') ? "Appending" : "Writing new",
3322 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3323 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3324 /* Make sure we do not exceed the output buffer size */
3325 strncat(buf, cfg.logfilename, 128);
3326 buf[strlen(buf)] = '\0';
3329 /* --- write header line iinto log file */
3330 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3333 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3335 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3339 void logfclose(void)