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 /* Product-order comparisons for rectangular block selection. */
90 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
91 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
93 static bufchain inbuf; /* terminal input buffer */
94 static pos curs; /* cursor */
95 static pos savecurs; /* saved cursor position */
96 static int marg_t, marg_b; /* scroll margins */
97 static int dec_om; /* DEC origin mode flag */
98 static int wrap, wrapnext; /* wrap flags */
99 static int insert; /* insert-mode flag */
100 static int cset; /* 0 or 1: which char set */
101 static int save_cset, save_csattr; /* saved with cursor position */
102 static int save_utf; /* saved with cursor position */
103 static int rvideo; /* global reverse video flag */
104 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
105 static int cursor_on; /* cursor enabled flag */
106 static int reset_132; /* Flag ESC c resets to 80 cols */
107 static int use_bce; /* Use Background coloured erase */
108 static int blinker; /* When blinking is the cursor on ? */
109 static int tblinker; /* When the blinking text is on */
110 static int blink_is_real; /* Actually blink blinking text */
111 static int term_echoing; /* Does terminal want local echo? */
112 static int term_editing; /* Does terminal want local edit? */
113 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
114 static int vt52_bold; /* Force bold on non-bold colours */
115 static int utf_state; /* Is there a pending UTF-8 character */
116 static int utf_char; /* and what is it so far. */
117 static int utf_size; /* The size of the UTF character. */
119 static int xterm_mouse; /* send mouse messages to app */
121 static unsigned long cset_attr[2];
124 * Saved settings on the alternate screen.
126 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
127 static int alt_t, alt_b;
128 static int alt_which;
130 #define ARGS_MAX 32 /* max # of esc sequence arguments */
131 #define ARG_DEFAULT 0 /* if an arg isn't specified */
132 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
133 static int esc_args[ARGS_MAX];
134 static int esc_nargs;
135 static int esc_query;
136 #define ANSI(x,y) ((x)+((y)<<8))
137 #define ANSI_QUE(x) ANSI(x,TRUE)
139 #define OSC_STR_MAX 2048
140 static int osc_strlen;
141 static char osc_string[OSC_STR_MAX + 1];
144 static char id_string[1024] = "\033[?6c";
146 static unsigned char *tabs;
158 OSC_STRING, OSC_MAYBE_ST,
167 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
170 LEXICOGRAPHIC, RECTANGULAR
173 SM_CHAR, SM_WORD, SM_LINE
175 static pos selstart, selend, selanchor;
177 static short wordness[256] = {
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
180 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
181 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
182 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
184 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
185 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
186 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
187 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
190 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
191 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
192 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
193 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
196 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
197 static wchar_t sel_nl[] = SEL_NL;
198 static wchar_t *paste_buffer = 0;
199 static int paste_len, paste_pos, paste_hold;
202 * Internal prototypes.
204 static void do_paint(Context, int);
205 static void erase_lots(int, int, int);
206 static void swap_screen(int);
207 static void update_sbar(void);
208 static void deselect(void);
211 * Resize a line to make it `cols' columns wide.
213 unsigned long *resizeline(unsigned long *line, int cols)
216 unsigned long lineattrs;
218 if (line[0] != (unsigned long)cols) {
220 * This line is the wrong length, which probably means it
221 * hasn't been accessed since a resize. Resize it now.
224 lineattrs = line[oldlen + 1];
225 line = srealloc(line, TSIZE * (2 + cols));
227 for (i = oldlen; i < cols; i++)
228 line[i + 1] = ERASE_CHAR;
229 line[cols + 1] = lineattrs & LATTR_MODE;
236 * Retrieve a line of the screen or of the scrollback, according to
237 * whether the y coordinate is non-negative or negative
240 unsigned long *lineptr(int y, int lineno)
242 unsigned long *line, *newline;
250 whichtree = scrollback;
251 treeindex = y + count234(scrollback);
253 line = index234(whichtree, treeindex);
255 /* We assume that we don't screw up and retrieve something out of range. */
256 assert(line != NULL);
258 newline = resizeline(line, cols);
259 if (newline != line) {
260 delpos234(whichtree, treeindex);
261 addpos234(whichtree, newline, treeindex);
268 #define lineptr(x) lineptr(x,__LINE__)
270 * Set up power-on settings for the terminal.
272 static void power_on(void)
274 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
277 alt_b = marg_b = rows - 1;
282 for (i = 0; i < cols; i++)
283 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
285 alt_om = dec_om = cfg.dec_om;
286 alt_wnext = wrapnext = alt_ins = insert = FALSE;
287 alt_wrap = wrap = cfg.wrap_mode;
290 alt_sco_acs = sco_acs = 0;
291 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
296 save_attr = curr_attr = ATTR_DEFAULT;
297 term_editing = term_echoing = FALSE;
298 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
299 app_cursor_keys = cfg.app_cursor;
300 app_keypad_keys = cfg.app_keypad;
302 blink_is_real = cfg.blinktext;
303 erase_char = ERASE_CHAR;
307 for (i = 0; i < 256; i++)
308 wordness[i] = cfg.wordness[i];
312 erase_lots(FALSE, TRUE, TRUE);
314 erase_lots(FALSE, TRUE, TRUE);
319 * Force a screen update.
321 void term_update(void)
326 int need_sbar_update = seen_disp_event;
327 if ((seen_key_event && (cfg.scroll_on_key)) ||
328 (seen_disp_event && (cfg.scroll_on_disp))) {
329 disptop = 0; /* return to main screen */
330 seen_disp_event = seen_key_event = 0;
331 need_sbar_update = TRUE;
333 if (need_sbar_update)
336 sys_cursor(curs.x, curs.y - disptop);
342 * Same as power_on(), but an external function.
344 void term_pwron(void)
354 * Clear the scrollback.
356 void term_clrsb(void)
360 while ((line = delpos234(scrollback, 0)) != NULL) {
367 * Initialise the terminal.
371 screen = alt_screen = scrollback = NULL;
373 disptext = dispcurs = NULL;
378 beephead = beeptail = NULL;
381 beep_overloaded = FALSE;
385 * Set up the terminal for a given size.
387 void term_size(int newrows, int newcols, int newsavelines)
390 unsigned long *newdisp, *line;
393 int save_alt_which = alt_which;
395 if (newrows == rows && newcols == cols && newsavelines == savelines)
396 return; /* nothing to do */
402 alt_b = marg_b = newrows - 1;
405 scrollback = newtree234(NULL);
406 screen = newtree234(NULL);
411 * Resize the screen and scrollback. We only need to shift
412 * lines around within our data structures, because lineptr()
413 * will take care of resizing each individual line if
416 * - If the new screen and the old screen differ in length, we
417 * must shunt some lines in from the scrollback or out to
420 * - If doing that fails to provide us with enough material to
421 * fill the new screen (i.e. the number of rows needed in
422 * the new screen exceeds the total number in the previous
423 * screen+scrollback), we must invent some blank lines to
426 * - Then, if the new scrollback length is less than the
427 * amount of scrollback we actually have, we must throw some
430 sblen = count234(scrollback);
431 /* Do this loop to expand the screen if newrows > rows */
432 for (i = rows; i < newrows; i++) {
434 line = delpos234(scrollback, --sblen);
436 line = smalloc(TSIZE * (newcols + 2));
438 for (j = 0; j <= newcols; j++)
439 line[j + 1] = ERASE_CHAR;
441 addpos234(screen, line, 0);
443 /* Do this loop to shrink the screen if newrows < rows */
444 for (i = newrows; i < rows; i++) {
445 line = delpos234(screen, 0);
446 addpos234(scrollback, line, sblen++);
448 assert(count234(screen) == newrows);
449 while (sblen > newsavelines) {
450 line = delpos234(scrollback, 0);
454 assert(count234(scrollback) <= newsavelines);
457 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
458 for (i = 0; i < newrows * (newcols + 1); i++)
459 newdisp[i] = ATTR_INVALID;
464 newalt = newtree234(NULL);
465 for (i = 0; i < newrows; i++) {
466 line = smalloc(TSIZE * (newcols + 2));
468 for (j = 0; j <= newcols; j++)
469 line[j + 1] = erase_char;
470 addpos234(newalt, line, i);
473 while (NULL != (line = delpos234(alt_screen, 0)))
475 freetree234(alt_screen);
479 tabs = srealloc(tabs, newcols * sizeof(*tabs));
482 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
483 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
487 curs.y += newrows - rows;
490 if (curs.y >= newrows)
491 curs.y = newrows - 1;
492 if (curs.x >= newcols)
493 curs.x = newcols - 1;
495 wrapnext = alt_wnext = FALSE;
499 savelines = newsavelines;
502 swap_screen(save_alt_which);
512 static void swap_screen(int which)
517 if (which == alt_which)
544 wrapnext = alt_wnext;
556 sco_acs = alt_sco_acs;
563 * Update the scroll bar.
565 static void update_sbar(void)
569 nscroll = count234(scrollback);
571 set_sbar(nscroll + rows, nscroll + disptop, rows);
575 * Check whether the region bounded by the two pointers intersects
576 * the scroll region, and de-select the on-screen selection if so.
578 static void check_selection(pos from, pos to)
580 if (poslt(from, selend) && poslt(selstart, to))
585 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
586 * for backward.) `sb' is TRUE if the scrolling is permitted to
587 * affect the scrollback buffer.
589 * NB this function invalidates all pointers into lines of the
590 * screen data structures. In particular, you MUST call fix_cpos
591 * after calling scroll() and before doing anything else that
592 * uses the cpos shortcut pointer.
594 static void scroll(int topline, int botline, int lines, int sb)
596 unsigned long *line, *line2;
599 if (topline != 0 || alt_which != 0)
604 line = delpos234(screen, botline);
605 line = resizeline(line, cols);
606 for (i = 0; i < cols; i++)
607 line[i + 1] = erase_char;
609 addpos234(screen, line, topline);
611 if (selstart.y >= topline && selstart.y <= botline) {
613 if (selstart.y > botline) {
614 selstart.y = botline;
618 if (selend.y >= topline && selend.y <= botline) {
620 if (selend.y > botline) {
630 line = delpos234(screen, topline);
631 if (sb && savelines > 0) {
632 int sblen = count234(scrollback);
634 * We must add this line to the scrollback. We'll
635 * remove a line from the top of the scrollback to
636 * replace it, or allocate a new one if the
637 * scrollback isn't full.
639 if (sblen == savelines) {
640 sblen--, line2 = delpos234(scrollback, 0);
642 line2 = smalloc(TSIZE * (cols + 2));
645 addpos234(scrollback, line, sblen);
649 * If the user is currently looking at part of the
650 * scrollback, and they haven't enabled any options
651 * that are going to reset the scrollback as a
652 * result of this movement, then the chances are
653 * they'd like to keep looking at the same line. So
654 * we move their viewpoint at the same rate as the
655 * scroll, at least until their viewpoint hits the
656 * top end of the scrollback buffer, at which point
657 * we don't have the choice any more.
659 * Thanks to Jan Holmen Holsten for the idea and
660 * initial implementation.
662 if (disptop > -savelines && disptop < 0)
665 line = resizeline(line, cols);
666 for (i = 0; i < cols; i++)
667 line[i + 1] = erase_char;
669 addpos234(screen, line, botline);
672 * If the selection endpoints move into the scrollback,
673 * we keep them moving until they hit the top. However,
674 * of course, if the line _hasn't_ moved into the
675 * scrollback then we don't do this, and cut them off
676 * at the top of the scroll region.
678 * This applies to selstart and selend (for an existing
679 * selection), and also selanchor (for one being
680 * selected as we speak).
682 seltop = sb ? -savelines : topline;
684 if (selstart.y >= seltop && selstart.y <= botline) {
686 if (selstart.y < seltop) {
691 if (selend.y >= seltop && selend.y <= botline) {
693 if (selend.y < seltop) {
698 if (selanchor.y >= seltop && selanchor.y <= botline) {
700 if (selanchor.y < seltop) {
701 selanchor.y = seltop;
712 * Move the cursor to a given position, clipping at boundaries. We
713 * may or may not want to clip at the scroll margin: marg_clip is 0
714 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
715 * even _being_ outside the margins.
717 static void move(int x, int y, int marg_clip)
724 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
726 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
740 * Save or restore the cursor and SGR mode.
742 static void save_cursor(int save)
746 save_attr = curr_attr;
749 save_csattr = cset_attr[cset];
750 save_sco_acs = sco_acs;
753 /* Make sure the window hasn't shrunk since the save */
759 curr_attr = save_attr;
762 cset_attr[cset] = save_csattr;
763 sco_acs = save_sco_acs;
766 erase_char = (' ' | ATTR_ASCII |
767 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
772 * Erase a large portion of the screen: the whole screen, or the
773 * whole line, or parts thereof.
775 static void erase_lots(int line_only, int from_begin, int to_end)
779 unsigned long *ldata;
801 check_selection(start, end);
803 /* Clear screen also forces a full window redraw, just in case. */
804 if (start.y == 0 && start.x == 0 && end.y == rows)
807 ldata = lineptr(start.y);
808 while (poslt(start, end)) {
809 if (start.x == cols && !erase_lattr)
810 ldata[start.x] &= ~LATTR_WRAPPED;
812 ldata[start.x] = erase_char;
813 if (incpos(start) && start.y < rows)
814 ldata = lineptr(start.y);
819 * Insert or delete characters within the current line. n is +ve if
820 * insertion is desired, and -ve for deletion.
822 static void insch(int n)
824 int dir = (n < 0 ? -1 : +1);
827 unsigned long *ldata;
829 n = (n < 0 ? -n : n);
830 if (n > cols - curs.x)
832 m = cols - curs.x - n;
834 cursplus.x = curs.x + n;
835 check_selection(curs, cursplus);
836 ldata = lineptr(curs.y);
838 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
840 ldata[curs.x + m++] = erase_char;
842 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
844 ldata[curs.x + n] = erase_char;
849 * Toggle terminal mode `mode' to state `state'. (`query' indicates
850 * whether the mode is a DEC private one or a normal one.)
852 static void toggle_mode(int mode, int query, int state)
858 case 1: /* application cursor keys */
859 app_cursor_keys = state;
861 case 2: /* VT52 mode */
864 blink_is_real = FALSE;
867 blink_is_real = cfg.blinktext;
870 case 3: /* 80/132 columns */
872 request_resize(state ? 132 : 80, rows);
875 case 5: /* reverse video */
877 * Toggle reverse video. If we receive an OFF within the
878 * visual bell timeout period after an ON, we trigger an
879 * effective visual bell, so that ESC[?5hESC[?5l will
880 * always be an actually _visible_ visual bell.
882 ticks = GetTickCount();
883 /* turn off a previous vbell to avoid inconsistencies */
884 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
886 if (rvideo && !state && /* we're turning it off... */
887 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
888 /* If there's no vbell timeout already, or this one lasts
889 * longer, replace vbell_timeout with ours. */
891 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
892 vbell_startpoint = rvbell_startpoint;
893 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
894 } else if (!rvideo && state) {
895 /* This is an ON, so we notice the time and save it. */
896 rvbell_startpoint = ticks;
899 seen_disp_event = TRUE;
903 case 6: /* DEC origin mode */
906 case 7: /* auto wrap */
909 case 8: /* auto key repeat */
912 case 10: /* set local edit mode */
913 term_editing = state;
914 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
916 case 25: /* enable/disable cursor */
917 compatibility2(OTHER, VT220);
919 seen_disp_event = TRUE;
921 case 47: /* alternate screen */
922 compatibility(OTHER);
927 case 1000: /* xterm mouse 1 */
928 xterm_mouse = state ? 1 : 0;
929 set_raw_mouse_mode(state);
931 case 1002: /* xterm mouse 2 */
932 xterm_mouse = state ? 2 : 0;
933 set_raw_mouse_mode(state);
937 case 4: /* set insert mode */
938 compatibility(VT102);
941 case 12: /* set echo mode */
942 term_echoing = !state;
943 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
945 case 20: /* Return sends ... */
946 cr_lf_return = state;
948 case 34: /* Make cursor BIG */
949 compatibility2(OTHER, VT220);
955 * Process an OSC sequence: set window title or icon name.
957 static void do_osc(void)
961 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
963 osc_string[osc_strlen] = '\0';
964 switch (esc_args[0]) {
967 set_icon(osc_string);
968 if (esc_args[0] == 1)
970 /* fall through: parameter 0 means set both */
973 set_title(osc_string);
980 * Remove everything currently in `inbuf' and stick it up on the
981 * in-memory display. There's a big state machine in here to
982 * process escape sequences...
987 unsigned char localbuf[256], *chars;
992 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
996 bufchain_prefix(&inbuf, &ret, &nchars);
997 if (nchars > sizeof(localbuf))
998 nchars = sizeof(localbuf);
999 memcpy(localbuf, ret, nchars);
1000 bufchain_consume(&inbuf, nchars);
1002 assert(chars != NULL);
1008 * Optionally log the session traffic to a file. Useful for
1009 * debugging and possibly also useful for actual logging.
1011 if (cfg.logtype == LGTYP_DEBUG)
1012 logtraffic((unsigned char) c, LGTYP_DEBUG);
1018 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1019 * be able to display 8-bit characters, but I'll let that go 'cause
1023 /* First see about all those translations. */
1024 if (termstate == TOPLEVEL) {
1026 switch (utf_state) {
1029 /* UTF-8 must be stateless so we ignore iso2022. */
1030 if (unitab_ctrl[c] != 0xFF)
1032 else c = ((unsigned char)c) | ATTR_ASCII;
1034 } else if ((c & 0xe0) == 0xc0) {
1035 utf_size = utf_state = 1;
1036 utf_char = (c & 0x1f);
1037 } else if ((c & 0xf0) == 0xe0) {
1038 utf_size = utf_state = 2;
1039 utf_char = (c & 0x0f);
1040 } else if ((c & 0xf8) == 0xf0) {
1041 utf_size = utf_state = 3;
1042 utf_char = (c & 0x07);
1043 } else if ((c & 0xfc) == 0xf8) {
1044 utf_size = utf_state = 4;
1045 utf_char = (c & 0x03);
1046 } else if ((c & 0xfe) == 0xfc) {
1047 utf_size = utf_state = 5;
1048 utf_char = (c & 0x01);
1059 if ((c & 0xC0) != 0x80) {
1065 utf_char = (utf_char << 6) | (c & 0x3f);
1071 /* Is somebody trying to be evil! */
1073 (c < 0x800 && utf_size >= 2) ||
1074 (c < 0x10000 && utf_size >= 3) ||
1075 (c < 0x200000 && utf_size >= 4) ||
1076 (c < 0x4000000 && utf_size >= 5))
1079 /* Unicode line separator and paragraph separator are CR-LF */
1080 if (c == 0x2028 || c == 0x2029)
1083 /* High controls are probably a Baaad idea too. */
1087 /* The UTF-16 surrogates are not nice either. */
1088 /* The standard give the option of decoding these:
1089 * I don't want to! */
1090 if (c >= 0xD800 && c < 0xE000)
1093 /* ISO 10646 characters now limited to UTF-16 range. */
1097 /* This is currently a TagPhobic application.. */
1098 if (c >= 0xE0000 && c <= 0xE007F)
1101 /* U+FEFF is best seen as a null. */
1104 /* But U+FFFE is an error. */
1105 if (c == 0xFFFE || c == 0xFFFF)
1108 /* Oops this is a 16bit implementation */
1113 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1115 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1117 if (sco_acs == 2) c ^= 0x80;
1120 switch (cset_attr[cset]) {
1122 * Linedraw characters are different from 'ESC ( B'
1123 * only for a small range. For ones outside that
1124 * range, make sure we use the same font as well as
1125 * the same encoding.
1128 if (unitab_ctrl[c] != 0xFF)
1131 c = ((unsigned char) c) | ATTR_LINEDRW;
1135 /* If UK-ASCII, make the '#' a LineDraw Pound */
1137 c = '}' | ATTR_LINEDRW;
1140 /*FALLTHROUGH*/ case ATTR_ASCII:
1141 if (unitab_ctrl[c] != 0xFF)
1144 c = ((unsigned char) c) | ATTR_ASCII;
1147 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1153 /* How about C1 controls ? */
1154 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1155 has_compat(VT220)) {
1156 termstate = SEEN_ESC;
1158 c = '@' + (c & 0x1F);
1161 /* Or the GL control. */
1162 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1163 if (curs.x && !wrapnext)
1167 *cpos = (' ' | curr_attr | ATTR_ASCII);
1169 /* Or normal C0 controls. */
1170 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1172 case '\005': /* terminal type query */
1173 /* Strictly speaking this is VT100 but a VT100 defaults to
1174 * no response. Other terminals respond at their option.
1176 * Don't put a CR in the default string as this tends to
1177 * upset some weird software.
1179 * An xterm returns "xterm" (5 characters)
1181 compatibility(ANSIMIN);
1183 char abuf[256], *s, *d;
1185 for (s = cfg.answerback, d = abuf; *s; s++) {
1187 if (*s >= 'a' && *s <= 'z')
1188 *d++ = (*s - ('a' - 1));
1189 else if ((*s >= '@' && *s <= '_') ||
1190 *s == '?' || (*s & 0x80))
1195 } else if (*s == '^') {
1200 lpage_send(CP_ACP, abuf, d - abuf, 0);
1205 struct beeptime *newbeep;
1206 unsigned long ticks;
1208 ticks = GetTickCount();
1210 if (!beep_overloaded) {
1211 newbeep = smalloc(sizeof(struct beeptime));
1212 newbeep->ticks = ticks;
1213 newbeep->next = NULL;
1217 beeptail->next = newbeep;
1223 * Throw out any beeps that happened more than
1227 beephead->ticks < ticks - cfg.bellovl_t) {
1228 struct beeptime *tmp = beephead;
1229 beephead = tmp->next;
1236 if (cfg.bellovl && beep_overloaded &&
1237 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1239 * If we're currently overloaded and the
1240 * last beep was more than s seconds ago,
1241 * leave overload mode.
1243 beep_overloaded = FALSE;
1244 } else if (cfg.bellovl && !beep_overloaded &&
1245 nbeeps >= cfg.bellovl_n) {
1247 * Now, if we have n or more beeps
1248 * remaining in the queue, go into overload
1251 beep_overloaded = TRUE;
1256 * Perform an actual beep if we're not overloaded.
1258 if (!cfg.bellovl || !beep_overloaded) {
1260 if (cfg.beep == BELL_VISUAL) {
1262 vbell_startpoint = ticks;
1270 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1271 else if (curs.x == 0 && curs.y > 0)
1272 curs.x = cols - 1, curs.y--;
1278 seen_disp_event = TRUE;
1281 compatibility(VT100);
1285 compatibility(VT100);
1290 termstate = VT52_ESC;
1292 compatibility(ANSIMIN);
1293 termstate = SEEN_ESC;
1301 seen_disp_event = TRUE;
1303 logtraffic((unsigned char) c, LGTYP_ASCII);
1306 if (has_compat(SCOANSI)) {
1308 erase_lots(FALSE, FALSE, TRUE);
1311 seen_disp_event = 1;
1315 compatibility(VT100);
1317 if (curs.y == marg_b)
1318 scroll(marg_t, marg_b, 1, TRUE);
1319 else if (curs.y < rows - 1)
1325 seen_disp_event = 1;
1327 logtraffic((unsigned char) c, LGTYP_ASCII);
1331 pos old_curs = curs;
1332 unsigned long *ldata = lineptr(curs.y);
1336 } while (curs.x < cols - 1 && !tabs[curs.x]);
1338 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1339 if (curs.x >= cols / 2)
1340 curs.x = cols / 2 - 1;
1347 check_selection(old_curs, curs);
1349 seen_disp_event = TRUE;
1353 switch (termstate) {
1355 /* Only graphic characters get this far, ctrls are stripped above */
1356 if (wrapnext && wrap) {
1357 cpos[1] |= LATTR_WRAPPED;
1358 if (curs.y == marg_b)
1359 scroll(marg_t, marg_b, 1, TRUE);
1360 else if (curs.y < rows - 1)
1368 if (selstate != NO_SELECTION) {
1369 pos cursplus = curs;
1371 check_selection(curs, cursplus);
1373 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1374 logtraffic((unsigned char) c, LGTYP_ASCII);
1376 extern int wcwidth(wchar_t ucs);
1381 width = wcwidth((wchar_t) c);
1384 *cpos++ = c | curr_attr;
1385 if (++curs.x == cols) {
1386 *cpos |= LATTR_WRAPPED;
1387 if (curs.y == marg_b)
1388 scroll(marg_t, marg_b, 1, TRUE);
1389 else if (curs.y < rows - 1)
1394 *cpos++ = UCSWIDE | curr_attr;
1397 *cpos++ = c | curr_attr;
1404 if (curs.x == cols) {
1408 if (wrap && vt52_mode) {
1409 cpos[1] |= LATTR_WRAPPED;
1410 if (curs.y == marg_b)
1411 scroll(marg_t, marg_b, 1, TRUE);
1412 else if (curs.y < rows - 1)
1419 seen_disp_event = 1;
1424 * This state is virtually identical to SEEN_ESC, with the
1425 * exception that we have an OSC sequence in the pipeline,
1426 * and _if_ we see a backslash, we process it.
1430 termstate = TOPLEVEL;
1433 /* else fall through */
1435 if (c >= ' ' && c <= '/') {
1442 termstate = TOPLEVEL;
1443 switch (ANSI(c, esc_query)) {
1444 case '[': /* enter CSI mode */
1445 termstate = SEEN_CSI;
1447 esc_args[0] = ARG_DEFAULT;
1450 case ']': /* xterm escape sequences */
1451 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1452 compatibility(OTHER);
1453 termstate = SEEN_OSC;
1456 case '7': /* save cursor */
1457 compatibility(VT100);
1460 case '8': /* restore cursor */
1461 compatibility(VT100);
1463 seen_disp_event = TRUE;
1466 compatibility(VT100);
1467 app_keypad_keys = TRUE;
1470 compatibility(VT100);
1471 app_keypad_keys = FALSE;
1473 case 'D': /* exactly equivalent to LF */
1474 compatibility(VT100);
1475 if (curs.y == marg_b)
1476 scroll(marg_t, marg_b, 1, TRUE);
1477 else if (curs.y < rows - 1)
1481 seen_disp_event = TRUE;
1483 case 'E': /* exactly equivalent to CR-LF */
1484 compatibility(VT100);
1486 if (curs.y == marg_b)
1487 scroll(marg_t, marg_b, 1, TRUE);
1488 else if (curs.y < rows - 1)
1492 seen_disp_event = TRUE;
1494 case 'M': /* reverse index - backwards LF */
1495 compatibility(VT100);
1496 if (curs.y == marg_t)
1497 scroll(marg_t, marg_b, -1, TRUE);
1498 else if (curs.y > 0)
1502 seen_disp_event = TRUE;
1504 case 'Z': /* terminal type query */
1505 compatibility(VT100);
1506 ldisc_send(id_string, strlen(id_string), 0);
1508 case 'c': /* restore power-on settings */
1509 compatibility(VT100);
1512 request_resize(80, rows);
1517 seen_disp_event = TRUE;
1519 case 'H': /* set a tab */
1520 compatibility(VT100);
1521 tabs[curs.x] = TRUE;
1524 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1525 compatibility(VT100);
1527 unsigned long *ldata;
1531 for (i = 0; i < rows; i++) {
1533 for (j = 0; j < cols; j++)
1534 ldata[j] = ATTR_DEFAULT | 'E';
1538 seen_disp_event = TRUE;
1539 scrtop.x = scrtop.y = 0;
1542 check_selection(scrtop, scrbot);
1546 case ANSI('3', '#'):
1547 case ANSI('4', '#'):
1548 case ANSI('5', '#'):
1549 case ANSI('6', '#'):
1550 compatibility(VT100);
1552 unsigned long nlattr;
1553 unsigned long *ldata;
1554 switch (ANSI(c, esc_query)) {
1555 case ANSI('3', '#'):
1558 case ANSI('4', '#'):
1561 case ANSI('5', '#'):
1562 nlattr = LATTR_NORM;
1564 default: /* spiritually case ANSI('6', '#'): */
1565 nlattr = LATTR_WIDE;
1568 ldata = lineptr(curs.y);
1569 ldata[cols] &= ~LATTR_MODE;
1570 ldata[cols] |= nlattr;
1574 case ANSI('A', '('):
1575 compatibility(VT100);
1576 cset_attr[0] = ATTR_GBCHR;
1578 case ANSI('B', '('):
1579 compatibility(VT100);
1580 cset_attr[0] = ATTR_ASCII;
1582 case ANSI('0', '('):
1583 compatibility(VT100);
1584 cset_attr[0] = ATTR_LINEDRW;
1586 case ANSI('U', '('):
1587 compatibility(OTHER);
1588 cset_attr[0] = ATTR_SCOACS;
1591 case ANSI('A', ')'):
1592 compatibility(VT100);
1593 cset_attr[1] = ATTR_GBCHR;
1595 case ANSI('B', ')'):
1596 compatibility(VT100);
1597 cset_attr[1] = ATTR_ASCII;
1599 case ANSI('0', ')'):
1600 compatibility(VT100);
1601 cset_attr[1] = ATTR_LINEDRW;
1603 case ANSI('U', ')'):
1604 compatibility(OTHER);
1605 cset_attr[1] = ATTR_SCOACS;
1608 case ANSI('8', '%'): /* Old Linux code */
1609 case ANSI('G', '%'):
1610 compatibility(OTHER);
1613 case ANSI('@', '%'):
1614 compatibility(OTHER);
1620 termstate = TOPLEVEL; /* default */
1622 if (esc_nargs <= ARGS_MAX) {
1623 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1624 esc_args[esc_nargs - 1] = 0;
1625 esc_args[esc_nargs - 1] =
1626 10 * esc_args[esc_nargs - 1] + c - '0';
1628 termstate = SEEN_CSI;
1629 } else if (c == ';') {
1630 if (++esc_nargs <= ARGS_MAX)
1631 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1632 termstate = SEEN_CSI;
1633 } else if (c < '@') {
1640 termstate = SEEN_CSI;
1642 switch (ANSI(c, esc_query)) {
1643 case 'A': /* move up N lines */
1644 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1645 seen_disp_event = TRUE;
1647 case 'e': /* move down N lines */
1648 compatibility(ANSI);
1651 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1652 seen_disp_event = TRUE;
1654 case ANSI('c', '>'): /* report xterm version */
1655 compatibility(OTHER);
1656 /* this reports xterm version 136 so that VIM can
1657 use the drag messages from the mouse reporting */
1658 ldisc_send("\033[>0;136;0c", 11, 0);
1660 case 'a': /* move right N cols */
1661 compatibility(ANSI);
1664 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1665 seen_disp_event = TRUE;
1667 case 'D': /* move left N cols */
1668 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1669 seen_disp_event = TRUE;
1671 case 'E': /* move down N lines and CR */
1672 compatibility(ANSI);
1673 move(0, curs.y + def(esc_args[0], 1), 1);
1674 seen_disp_event = TRUE;
1676 case 'F': /* move up N lines and CR */
1677 compatibility(ANSI);
1678 move(0, curs.y - def(esc_args[0], 1), 1);
1679 seen_disp_event = TRUE;
1682 case '`': /* set horizontal posn */
1683 compatibility(ANSI);
1684 move(def(esc_args[0], 1) - 1, curs.y, 0);
1685 seen_disp_event = TRUE;
1687 case 'd': /* set vertical posn */
1688 compatibility(ANSI);
1690 (dec_om ? marg_t : 0) + def(esc_args[0],
1693 seen_disp_event = TRUE;
1696 case 'f': /* set horz and vert posns at once */
1698 esc_args[1] = ARG_DEFAULT;
1699 move(def(esc_args[1], 1) - 1,
1700 (dec_om ? marg_t : 0) + def(esc_args[0],
1703 seen_disp_event = TRUE;
1705 case 'J': /* erase screen or parts of it */
1707 unsigned int i = def(esc_args[0], 0) + 1;
1710 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1713 seen_disp_event = TRUE;
1715 case 'K': /* erase line or parts of it */
1717 unsigned int i = def(esc_args[0], 0) + 1;
1720 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1722 seen_disp_event = TRUE;
1724 case 'L': /* insert lines */
1725 compatibility(VT102);
1726 if (curs.y <= marg_b)
1727 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1730 seen_disp_event = TRUE;
1732 case 'M': /* delete lines */
1733 compatibility(VT102);
1734 if (curs.y <= marg_b)
1735 scroll(curs.y, marg_b, def(esc_args[0], 1),
1738 seen_disp_event = TRUE;
1740 case '@': /* insert chars */
1741 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1742 compatibility(VT102);
1743 insch(def(esc_args[0], 1));
1744 seen_disp_event = TRUE;
1746 case 'P': /* delete chars */
1747 compatibility(VT102);
1748 insch(-def(esc_args[0], 1));
1749 seen_disp_event = TRUE;
1751 case 'c': /* terminal type query */
1752 compatibility(VT100);
1753 /* This is the response for a VT102 */
1754 ldisc_send(id_string, strlen(id_string), 0);
1756 case 'n': /* cursor position query */
1757 if (esc_args[0] == 6) {
1759 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1761 ldisc_send(buf, strlen(buf), 0);
1762 } else if (esc_args[0] == 5) {
1763 ldisc_send("\033[0n", 4, 0);
1766 case 'h': /* toggle modes to high */
1768 compatibility(VT100);
1771 for (i = 0; i < esc_nargs; i++)
1772 toggle_mode(esc_args[i], esc_query, TRUE);
1775 case 'l': /* toggle modes to low */
1777 compatibility(VT100);
1780 for (i = 0; i < esc_nargs; i++)
1781 toggle_mode(esc_args[i], esc_query, FALSE);
1784 case 'g': /* clear tabs */
1785 compatibility(VT100);
1786 if (esc_nargs == 1) {
1787 if (esc_args[0] == 0) {
1788 tabs[curs.x] = FALSE;
1789 } else if (esc_args[0] == 3) {
1791 for (i = 0; i < cols; i++)
1796 case 'r': /* set scroll margins */
1797 compatibility(VT100);
1798 if (esc_nargs <= 2) {
1800 top = def(esc_args[0], 1) - 1;
1801 bot = (esc_nargs <= 1
1803 0 ? rows : def(esc_args[1], rows)) - 1;
1806 /* VTTEST Bug 9 - if region is less than 2 lines
1807 * don't change region.
1809 if (bot - top > 0) {
1814 * I used to think the cursor should be
1815 * placed at the top of the newly marginned
1816 * area. Apparently not: VMS TPU falls over
1819 * Well actually it should for Origin mode - RDB
1821 curs.y = (dec_om ? marg_t : 0);
1823 seen_disp_event = TRUE;
1827 case 'm': /* set graphics rendition */
1830 * A VT100 without the AVO only had one attribute, either
1831 * underline or reverse video depending on the cursor type,
1832 * this was selected by CSI 7m.
1835 * This is sometimes DIM, eg on the GIGI and Linux
1837 * This is sometimes INVIS various ANSI.
1839 * This like 22 disables BOLD, DIM and INVIS
1841 * The ANSI colours appear on any terminal that has colour
1842 * (obviously) but the interaction between sgr0 and the
1843 * colours varies but is usually related to the background
1844 * colour erase item.
1845 * The interaction between colour attributes and the mono
1846 * ones is also very implementation dependent.
1848 * The 39 and 49 attributes are likely to be unimplemented.
1851 for (i = 0; i < esc_nargs; i++) {
1852 switch (def(esc_args[i], 0)) {
1853 case 0: /* restore defaults */
1854 curr_attr = ATTR_DEFAULT;
1856 case 1: /* enable bold */
1857 compatibility(VT100AVO);
1858 curr_attr |= ATTR_BOLD;
1860 case 21: /* (enable double underline) */
1861 compatibility(OTHER);
1862 case 4: /* enable underline */
1863 compatibility(VT100AVO);
1864 curr_attr |= ATTR_UNDER;
1866 case 5: /* enable blink */
1867 compatibility(VT100AVO);
1868 curr_attr |= ATTR_BLINK;
1870 case 7: /* enable reverse video */
1871 curr_attr |= ATTR_REVERSE;
1873 case 10: /* SCO acs off */
1874 compatibility(SCOANSI);
1876 case 11: /* SCO acs on */
1877 compatibility(SCOANSI);
1879 case 12: /* SCO acs on flipped */
1880 compatibility(SCOANSI);
1882 case 22: /* disable bold */
1883 compatibility2(OTHER, VT220);
1884 curr_attr &= ~ATTR_BOLD;
1886 case 24: /* disable underline */
1887 compatibility2(OTHER, VT220);
1888 curr_attr &= ~ATTR_UNDER;
1890 case 25: /* disable blink */
1891 compatibility2(OTHER, VT220);
1892 curr_attr &= ~ATTR_BLINK;
1894 case 27: /* disable reverse video */
1895 compatibility2(OTHER, VT220);
1896 curr_attr &= ~ATTR_REVERSE;
1907 curr_attr &= ~ATTR_FGMASK;
1909 (esc_args[i] - 30) << ATTR_FGSHIFT;
1911 case 39: /* default-foreground */
1912 curr_attr &= ~ATTR_FGMASK;
1913 curr_attr |= ATTR_DEFFG;
1924 curr_attr &= ~ATTR_BGMASK;
1926 (esc_args[i] - 40) << ATTR_BGSHIFT;
1928 case 49: /* default-background */
1929 curr_attr &= ~ATTR_BGMASK;
1930 curr_attr |= ATTR_DEFBG;
1935 erase_char = (' ' | ATTR_ASCII |
1937 (ATTR_FGMASK | ATTR_BGMASK)));
1940 case 's': /* save cursor */
1943 case 'u': /* restore cursor */
1945 seen_disp_event = TRUE;
1947 case 't': /* set page size - ie window height */
1949 * VT340/VT420 sequence DECSLPP, DEC only allows values
1950 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1951 * illegal values (eg first arg 1..9) for window changing
1955 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1956 compatibility(VT340TEXT);
1957 request_resize(cols, def(esc_args[0], 24));
1959 } else if (esc_nargs >= 1 &&
1962 compatibility(OTHER);
1964 switch (esc_args[0]) {
1974 if (esc_nargs >= 3) {
1975 move_window(def(esc_args[1], 0),
1976 def(esc_args[2], 0));
1980 /* We should resize the window to a given
1981 * size in pixels here, but currently our
1982 * resizing code isn't healthy enough to
1986 set_zorder(TRUE); /* move to top */
1989 set_zorder(FALSE); /* move to bottom */
1995 if (esc_nargs >= 3) {
1996 request_resize(def(esc_args[2], cfg.width),
1997 def(esc_args[1], cfg.height));
2002 set_zoomed(esc_args[1] ? TRUE : FALSE);
2005 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2009 get_window_pos(&x, &y);
2010 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2011 ldisc_send(buf, len, 0);
2014 get_window_pixels(&x, &y);
2015 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2016 ldisc_send(buf, len, 0);
2019 len = sprintf(buf, "\033[8;%d;%dt",
2021 ldisc_send(buf, len, 0);
2025 * Hmmm. Strictly speaking we
2026 * should return `the size of the
2027 * screen in characters', but
2028 * that's not easy: (a) window
2029 * furniture being what it is it's
2030 * hard to compute, and (b) in
2031 * resize-font mode maximising the
2032 * window wouldn't change the
2033 * number of characters. *shrug*. I
2034 * think we'll ignore it for the
2035 * moment and see if anyone
2036 * complains, and then ask them
2037 * what they would like it to do.
2041 p = get_window_title(TRUE);
2043 ldisc_send("\033]L", 3, 0);
2044 ldisc_send(p, len, 0);
2045 ldisc_send("\033\\", 2, 0);
2048 p = get_window_title(FALSE);
2050 ldisc_send("\033]l", 3, 0);
2051 ldisc_send(p, len, 0);
2052 ldisc_send("\033\\", 2, 0);
2058 compatibility(SCOANSI);
2059 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2062 seen_disp_event = TRUE;
2065 compatibility(SCOANSI);
2066 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2069 seen_disp_event = TRUE;
2071 case ANSI('|', '*'):
2072 /* VT420 sequence DECSNLS
2073 * Set number of lines on screen
2074 * VT420 uses VGA like hardware and can support any size in
2075 * reasonable range (24..49 AIUI) with no default specified.
2077 compatibility(VT420);
2078 if (esc_nargs == 1 && esc_args[0] > 0) {
2079 request_resize(cols, def(esc_args[0], cfg.height));
2083 case ANSI('|', '$'):
2084 /* VT340/VT420 sequence DECSCPP
2085 * Set number of columns per page
2086 * Docs imply range is only 80 or 132, but I'll allow any.
2088 compatibility(VT340TEXT);
2089 if (esc_nargs <= 1) {
2090 request_resize(def(esc_args[0], cfg.width), rows);
2094 case 'X': /* write N spaces w/o moving cursor */
2095 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2096 compatibility(ANSIMIN);
2098 int n = def(esc_args[0], 1);
2100 unsigned long *p = cpos;
2101 if (n > cols - curs.x)
2105 check_selection(curs, cursplus);
2108 seen_disp_event = TRUE;
2111 case 'x': /* report terminal characteristics */
2112 compatibility(VT100);
2115 int i = def(esc_args[0], 0);
2116 if (i == 0 || i == 1) {
2117 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2119 ldisc_send(buf, 20, 0);
2123 case 'Z': /* BackTab for xterm */
2124 compatibility(OTHER);
2126 int i = def(esc_args[0], 1);
2127 pos old_curs = curs;
2129 for(;i>0 && curs.x>0; i--) {
2132 } while (curs.x >0 && !tabs[curs.x]);
2135 check_selection(old_curs, curs);
2138 case ANSI('L', '='):
2139 compatibility(OTHER);
2140 use_bce = (esc_args[0] <= 0);
2141 erase_char = ERASE_CHAR;
2143 erase_char = (' ' | ATTR_ASCII |
2145 (ATTR_FGMASK | ATTR_BGMASK)));
2147 case ANSI('E', '='):
2148 compatibility(OTHER);
2149 blink_is_real = (esc_args[0] >= 1);
2151 case ANSI('p', '"'):
2152 /* Allow the host to make this emulator a 'perfect' VT102.
2153 * This first appeared in the VT220, but we do need to get
2154 * back to PuTTY mode so I won't check it.
2156 * The arg in 40..42,50 are a PuTTY extension.
2157 * The 2nd arg, 8bit vs 7bit is not checked.
2159 * Setting VT102 mode should also change the Fkeys to
2160 * generate PF* codes as a real VT102 has no Fkeys.
2161 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2164 * Note ESC c will NOT change this!
2167 switch (esc_args[0]) {
2169 compatibility_level &= ~TM_VTXXX;
2170 compatibility_level |= TM_VT102;
2173 compatibility_level &= ~TM_VTXXX;
2174 compatibility_level |= TM_VT220;
2178 if (esc_args[0] > 60 && esc_args[0] < 70)
2179 compatibility_level |= TM_VTXXX;
2183 compatibility_level &= TM_VTXXX;
2186 compatibility_level = TM_PUTTY;
2189 compatibility_level = TM_SCOANSI;
2193 compatibility_level = TM_PUTTY;
2199 /* Change the response to CSI c */
2200 if (esc_args[0] == 50) {
2203 strcpy(id_string, "\033[?");
2204 for (i = 1; i < esc_nargs; i++) {
2206 strcat(id_string, ";");
2207 sprintf(lbuf, "%d", esc_args[i]);
2208 strcat(id_string, lbuf);
2210 strcat(id_string, "c");
2213 /* Is this a good idea ?
2214 * Well we should do a soft reset at this point ...
2216 if (!has_compat(VT420) && has_compat(VT100)) {
2218 request_resize(132, 24);
2220 request_resize(80, 24);
2229 case 'P': /* Linux palette sequence */
2230 termstate = SEEN_OSC_P;
2233 case 'R': /* Linux palette reset */
2236 termstate = TOPLEVEL;
2238 case 'W': /* word-set */
2239 termstate = SEEN_OSC_W;
2252 esc_args[0] = 10 * esc_args[0] + c - '0';
2256 * Grotty hack to support xterm and DECterm title
2257 * sequences concurrently.
2259 if (esc_args[0] == 2) {
2263 /* else fall through */
2265 termstate = OSC_STRING;
2271 * This OSC stuff is EVIL. It takes just one character to get into
2272 * sysline mode and it's not initially obvious how to get out.
2273 * So I've added CR and LF as string aborts.
2274 * This shouldn't effect compatibility as I believe embedded
2275 * control characters are supposed to be interpreted (maybe?)
2276 * and they don't display anything useful anyway.
2280 if (c == '\n' || c == '\r') {
2281 termstate = TOPLEVEL;
2282 } else if (c == 0234 || c == '\007') {
2284 * These characters terminate the string; ST and BEL
2285 * terminate the sequence and trigger instant
2286 * processing of it, whereas ESC goes back to SEEN_ESC
2287 * mode unless it is followed by \, in which case it is
2288 * synonymous with ST in the first place.
2291 termstate = TOPLEVEL;
2292 } else if (c == '\033')
2293 termstate = OSC_MAYBE_ST;
2294 else if (osc_strlen < OSC_STR_MAX)
2295 osc_string[osc_strlen++] = c;
2299 int max = (osc_strlen == 0 ? 21 : 16);
2301 if (c >= '0' && c <= '9')
2303 else if (c >= 'A' && c <= 'A' + max - 10)
2305 else if (c >= 'a' && c <= 'a' + max - 10)
2308 termstate = TOPLEVEL;
2311 osc_string[osc_strlen++] = val;
2312 if (osc_strlen >= 7) {
2313 palette_set(osc_string[0],
2314 osc_string[1] * 16 + osc_string[2],
2315 osc_string[3] * 16 + osc_string[4],
2316 osc_string[5] * 16 + osc_string[6]);
2318 termstate = TOPLEVEL;
2334 esc_args[0] = 10 * esc_args[0] + c - '0';
2337 termstate = OSC_STRING;
2342 termstate = TOPLEVEL;
2343 seen_disp_event = TRUE;
2346 move(curs.x, curs.y - 1, 1);
2349 move(curs.x, curs.y + 1, 1);
2352 move(curs.x + 1, curs.y, 1);
2355 move(curs.x - 1, curs.y, 1);
2358 * From the VT100 Manual
2359 * NOTE: The special graphics characters in the VT100
2360 * are different from those in the VT52
2362 * From VT102 manual:
2363 * 137 _ Blank - Same
2364 * 140 ` Reserved - Humm.
2365 * 141 a Solid rectangle - Similar
2366 * 142 b 1/ - Top half of fraction for the
2367 * 143 c 3/ - subscript numbers below.
2370 * 146 f Degrees - Same
2371 * 147 g Plus or minus - Same
2373 * 151 i Ellipsis (dots)
2376 * 154 l Bar at scan 0
2377 * 155 m Bar at scan 1
2378 * 156 n Bar at scan 2
2379 * 157 o Bar at scan 3 - Similar
2380 * 160 p Bar at scan 4 - Similar
2381 * 161 q Bar at scan 5 - Similar
2382 * 162 r Bar at scan 6 - Same
2383 * 163 s Bar at scan 7 - Similar
2398 cset_attr[cset = 0] = ATTR_LINEDRW;
2401 cset_attr[cset = 0] = ATTR_ASCII;
2408 scroll(0, rows - 1, -1, TRUE);
2409 else if (curs.y > 0)
2415 erase_lots(FALSE, FALSE, TRUE);
2419 erase_lots(TRUE, FALSE, TRUE);
2423 /* XXX Print cursor line */
2426 /* XXX Start controller mode */
2429 /* XXX Stop controller mode */
2433 termstate = VT52_Y1;
2436 ldisc_send("\033/Z", 3, 0);
2439 app_keypad_keys = TRUE;
2442 app_keypad_keys = FALSE;
2445 /* XXX This should switch to VT100 mode not current or default
2446 * VT mode. But this will only have effect in a VT220+
2450 blink_is_real = cfg.blinktext;
2454 /* XXX Enter auto print mode */
2457 /* XXX Exit auto print mode */
2460 /* XXX Print screen */
2466 /* compatibility(ATARI) */
2468 erase_lots(FALSE, FALSE, TRUE);
2472 /* compatibility(ATARI) */
2473 if (curs.y <= marg_b)
2474 scroll(curs.y, marg_b, -1, FALSE);
2477 /* compatibility(ATARI) */
2478 if (curs.y <= marg_b)
2479 scroll(curs.y, marg_b, 1, TRUE);
2482 /* compatibility(ATARI) */
2483 termstate = VT52_FG;
2486 /* compatibility(ATARI) */
2487 termstate = VT52_BG;
2490 /* compatibility(ATARI) */
2491 erase_lots(FALSE, TRUE, FALSE);
2495 /* compatibility(ATARI) */
2499 /* compatibility(ATARI) */
2502 /* case 'j': Save cursor position - broken on ST */
2503 /* case 'k': Restore cursor position */
2505 /* compatibility(ATARI) */
2506 erase_lots(TRUE, TRUE, TRUE);
2512 /* compatibility(ATARI) */
2513 erase_lots(TRUE, TRUE, FALSE);
2516 /* compatibility(ATARI) */
2517 curr_attr |= ATTR_REVERSE;
2520 /* compatibility(ATARI) */
2521 curr_attr &= ~ATTR_REVERSE;
2523 case 'v': /* wrap Autowrap on - Wyse style */
2524 /* compatibility(ATARI) */
2527 case 'w': /* Autowrap off */
2528 /* compatibility(ATARI) */
2533 /* compatibility(OTHER) */
2535 curr_attr = ATTR_DEFAULT;
2537 erase_char = (' ' | ATTR_ASCII |
2539 (ATTR_FGMASK | ATTR_BGMASK)));
2542 /* compatibility(VI50) */
2543 curr_attr |= ATTR_UNDER;
2546 /* compatibility(VI50) */
2547 curr_attr &= ~ATTR_UNDER;
2550 /* compatibility(VI50) */
2552 curr_attr |= ATTR_BOLD;
2555 /* compatibility(VI50) */
2557 curr_attr &= ~ATTR_BOLD;
2563 termstate = VT52_Y2;
2564 move(curs.x, c - ' ', 0);
2567 termstate = TOPLEVEL;
2568 move(c - ' ', curs.y, 0);
2573 termstate = TOPLEVEL;
2574 curr_attr &= ~ATTR_FGMASK;
2575 curr_attr &= ~ATTR_BOLD;
2576 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2577 if ((c & 0x8) || vt52_bold)
2578 curr_attr |= ATTR_BOLD;
2581 erase_char = (' ' | ATTR_ASCII |
2582 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2585 termstate = TOPLEVEL;
2586 curr_attr &= ~ATTR_BGMASK;
2587 curr_attr &= ~ATTR_BLINK;
2588 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2590 /* Note: bold background */
2592 curr_attr |= ATTR_BLINK;
2595 erase_char = (' ' | ATTR_ASCII |
2596 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2599 default: break; /* placate gcc warning about enum use */
2601 if (selstate != NO_SELECTION) {
2602 pos cursplus = curs;
2604 check_selection(curs, cursplus);
2611 * Compare two lines to determine whether they are sufficiently
2612 * alike to scroll-optimise one to the other. Return the degree of
2615 static int linecmp(unsigned long *a, unsigned long *b)
2619 for (i = n = 0; i < cols; i++)
2620 n += (*a++ == *b++);
2626 * Given a context, update the window. Out of paranoia, we don't
2627 * allow WM_PAINT responses to do scrolling optimisations.
2629 static void do_paint(Context ctx, int may_optimise)
2631 int i, j, our_curs_y;
2632 unsigned long rv, cursor;
2635 long cursor_background = ERASE_CHAR;
2636 unsigned long ticks;
2639 * Check the visual bell state.
2642 ticks = GetTickCount();
2643 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2647 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2650 * screen array, disptop, scrtop,
2652 * cfg.blinkpc, blink_is_real, tblinker,
2653 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2656 /* Has the cursor position or type changed ? */
2659 if (blinker || !cfg.blink_cur)
2660 cursor = TATTR_ACTCURS;
2664 cursor = TATTR_PASCURS;
2666 cursor |= TATTR_RIGHTCURS;
2669 our_curs_y = curs.y - disptop;
2671 if (dispcurs && (curstype != cursor ||
2673 disptext + our_curs_y * (cols + 1) + curs.x)) {
2674 if (dispcurs > disptext &&
2675 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2676 dispcurs[-1] |= ATTR_INVALID;
2677 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2678 dispcurs[1] |= ATTR_INVALID;
2679 *dispcurs |= ATTR_INVALID;
2684 /* The normal screen data */
2685 for (i = 0; i < rows; i++) {
2686 unsigned long *ldata;
2688 int idx, dirty_line, dirty_run, selected;
2689 unsigned long attr = 0;
2690 int updated_line = 0;
2693 int last_run_dirty = 0;
2695 scrpos.y = i + disptop;
2696 ldata = lineptr(scrpos.y);
2697 lattr = (ldata[cols] & LATTR_MODE);
2699 idx = i * (cols + 1);
2700 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2701 disptext[idx + cols] = ldata[cols];
2703 for (j = 0; j < cols; j++, idx++) {
2704 unsigned long tattr, tchar;
2705 unsigned long *d = ldata + j;
2709 tchar = (*d & (CHAR_MASK | CSET_MASK));
2710 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2711 switch (tchar & CSET_MASK) {
2713 tchar = unitab_line[tchar & 0xFF];
2716 tchar = unitab_xterm[tchar & 0xFF];
2719 tchar = unitab_scoacs[tchar&0xFF];
2722 tattr |= (tchar & CSET_MASK);
2724 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2727 /* Video reversing things */
2728 if (seltype == LEXICOGRAPHIC)
2729 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2731 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2733 ^ (selected ? ATTR_REVERSE : 0));
2735 /* 'Real' blinking ? */
2736 if (blink_is_real && (tattr & ATTR_BLINK)) {
2737 if (has_focus && tblinker) {
2739 tattr &= ~CSET_MASK;
2742 tattr &= ~ATTR_BLINK;
2746 * Check the font we'll _probably_ be using to see if
2747 * the character is wide when we don't want it to be.
2749 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2750 if ((tattr & ATTR_WIDE) == 0 &&
2751 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2752 tattr |= ATTR_NARROW;
2753 } else if (disptext[idx]&ATTR_NARROW)
2754 tattr |= ATTR_NARROW;
2756 /* Cursor here ? Save the 'background' */
2757 if (i == our_curs_y && j == curs.x) {
2758 cursor_background = tattr | tchar;
2759 dispcurs = disptext + idx;
2762 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2765 break_run = (tattr != attr || j - start >= sizeof(ch));
2767 /* Special hack for VT100 Linedraw glyphs */
2768 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2769 && tchar <= 0xBD) break_run = TRUE;
2771 if (!dbcs_screenfont && !dirty_line) {
2772 if ((tchar | tattr) == disptext[idx])
2774 else if (!dirty_run && ccount == 1)
2779 if ((dirty_run || last_run_dirty) && ccount > 0) {
2780 do_text(ctx, start, i, ch, ccount, attr, lattr);
2786 if (dbcs_screenfont)
2787 last_run_dirty = dirty_run;
2788 dirty_run = dirty_line;
2791 if ((tchar | tattr) != disptext[idx])
2793 ch[ccount++] = (char) tchar;
2794 disptext[idx] = tchar | tattr;
2796 /* If it's a wide char step along to the next one. */
2797 if (tattr & ATTR_WIDE) {
2801 /* Cursor is here ? Ouch! */
2802 if (i == our_curs_y && j == curs.x) {
2803 cursor_background = *d;
2804 dispcurs = disptext + idx;
2806 if (disptext[idx] != *d)
2812 if (dirty_run && ccount > 0) {
2813 do_text(ctx, start, i, ch, ccount, attr, lattr);
2817 /* Cursor on this line ? (and changed) */
2818 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2819 ch[0] = (char) (cursor_background & CHAR_MASK);
2820 attr = (cursor_background & ATTR_MASK) | cursor;
2821 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2828 * Flick the switch that says if blinking things should be shown or hidden.
2831 void term_blink(int flg)
2833 static long last_blink = 0;
2834 static long last_tblink = 0;
2835 long now, blink_diff;
2837 now = GetTickCount();
2838 blink_diff = now - last_tblink;
2840 /* Make sure the text blinks no more than 2Hz */
2841 if (blink_diff < 0 || blink_diff > 450) {
2843 tblinker = !tblinker;
2852 blink_diff = now - last_blink;
2854 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2855 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2863 * Invalidate the whole screen so it will be repainted in full.
2865 void term_invalidate(void)
2869 for (i = 0; i < rows * (cols + 1); i++)
2870 disptext[i] = ATTR_INVALID;
2874 * Paint the window in response to a WM_PAINT message.
2876 void term_paint(Context ctx, int left, int top, int right, int bottom)
2879 if (left < 0) left = 0;
2880 if (top < 0) top = 0;
2881 if (right >= cols) right = cols-1;
2882 if (bottom >= rows) bottom = rows-1;
2884 for (i = top; i <= bottom && i < rows; i++) {
2885 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2886 for (j = left; j <= right && j < cols; j++)
2887 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2889 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2890 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2893 /* This should happen soon enough, also for some reason it sometimes
2894 * fails to actually do anything when re-sizing ... painting the wrong
2898 do_paint (ctx, FALSE);
2902 * Attempt to scroll the scrollback. The second parameter gives the
2903 * position we want to scroll to; the first is +1 to denote that
2904 * this position is relative to the beginning of the scrollback, -1
2905 * to denote it is relative to the end, and 0 to denote that it is
2906 * relative to the current position.
2908 void term_scroll(int rel, int where)
2910 int sbtop = -count234(scrollback);
2912 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2913 if (disptop < sbtop)
2921 static void clipme(pos top, pos bottom, int rect)
2924 wchar_t *wbptr; /* where next char goes within workbuf */
2926 int wblen = 0; /* workbuf len */
2927 int buflen; /* amount of memory allocated to workbuf */
2929 buflen = 5120; /* Default size */
2930 workbuf = smalloc(buflen * sizeof(wchar_t));
2931 wbptr = workbuf; /* start filling here */
2932 old_top_x = top.x; /* needed for rect==1 */
2934 while (poslt(top, bottom)) {
2936 unsigned long *ldata = lineptr(top.y);
2940 * nlpos will point at the maximum position on this line we
2941 * should copy up to. So we start it at the end of the
2948 * ... move it backwards if there's unused space at the end
2949 * of the line (and also set `nl' if this is the case,
2950 * because in normal selection mode this means we need a
2951 * newline at the end)...
2953 if (!(ldata[cols] & LATTR_WRAPPED)) {
2954 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2955 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2956 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2957 && poslt(top, nlpos))
2959 if (poslt(nlpos, bottom))
2964 * ... and then clip it to the terminal x coordinate if
2965 * we're doing rectangular selection. (In this case we
2966 * still did the above, so that copying e.g. the right-hand
2967 * column from a table doesn't fill with spaces on the
2971 if (nlpos.x > bottom.x)
2973 nl = (top.y < bottom.y);
2976 while (poslt(top, bottom) && poslt(top, nlpos)) {
2979 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2981 wchar_t cbuf[16], *p;
2982 int uc = (ldata[top.x] & 0xFFFF);
2985 if (uc == UCSWIDE) {
2990 switch (uc & CSET_MASK) {
2993 uc = unitab_xterm[uc & 0xFF];
2997 uc = unitab_line[uc & 0xFF];
3000 uc = unitab_scoacs[uc&0xFF];
3003 switch (uc & CSET_MASK) {
3005 uc = unitab_font[uc & 0xFF];
3008 uc = unitab_oemcp[uc & 0xFF];
3012 set = (uc & CSET_MASK);
3013 c = (uc & CHAR_MASK);
3017 if (DIRECT_FONT(uc)) {
3018 if (c >= ' ' && c != 0x7F) {
3019 unsigned char buf[4];
3022 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
3024 buf[1] = (unsigned char) ldata[top.x + 1];
3025 rv = MultiByteToWideChar(font_codepage,
3026 0, buf, 2, wbuf, 4);
3030 rv = MultiByteToWideChar(font_codepage,
3031 0, buf, 1, wbuf, 4);
3035 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3042 for (p = cbuf; *p; p++) {
3043 /* Enough overhead for trailing NL and nul */
3044 if (wblen >= buflen - 16) {
3047 sizeof(wchar_t) * (buflen += 100));
3048 wbptr = workbuf + wblen;
3057 for (i = 0; i < sel_nl_sz; i++) {
3059 *wbptr++ = sel_nl[i];
3063 top.x = rect ? old_top_x : 0;
3067 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3068 if (buflen > 0) /* indicates we allocated this buffer */
3072 void term_copyall(void)
3075 top.y = -count234(scrollback);
3077 clipme(top, curs, 0);
3081 * The wordness array is mainly for deciding the disposition of the US-ASCII
3084 static int wordtype(int uc)
3087 int start, end, ctype;
3088 } *wptr, ucs_words[] = {
3094 0x037e, 0x037e, 1}, /* Greek question mark */
3096 0x0387, 0x0387, 1}, /* Greek ano teleia */
3098 0x055a, 0x055f, 1}, /* Armenian punctuation */
3100 0x0589, 0x0589, 1}, /* Armenian full stop */
3102 0x0700, 0x070d, 1}, /* Syriac punctuation */
3104 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3106 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3108 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3110 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3112 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3114 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3116 0x2000, 0x200a, 0}, /* Various spaces */
3118 0x2070, 0x207f, 2}, /* superscript */
3120 0x2080, 0x208f, 2}, /* subscript */
3122 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3124 0x3000, 0x3000, 0}, /* ideographic space */
3126 0x3001, 0x3020, 1}, /* ideographic punctuation */
3128 0x303f, 0x309f, 3}, /* Hiragana */
3130 0x30a0, 0x30ff, 3}, /* Katakana */
3132 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3134 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3136 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3138 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3140 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3142 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3144 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3146 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3148 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3153 uc &= (CSET_MASK | CHAR_MASK);
3155 switch (uc & CSET_MASK) {
3157 uc = unitab_xterm[uc & 0xFF];
3160 uc = unitab_line[uc & 0xFF];
3163 uc = unitab_scoacs[uc&0xFF];
3166 switch (uc & CSET_MASK) {
3168 uc = unitab_font[uc & 0xFF];
3171 uc = unitab_oemcp[uc & 0xFF];
3175 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3176 * fail as there's such a thing as a double width space. :-(
3178 if (dbcs_screenfont && font_codepage == line_codepage)
3182 return wordness[uc];
3184 for (wptr = ucs_words; wptr->start; wptr++) {
3185 if (uc >= wptr->start && uc <= wptr->end)
3193 * Spread the selection outwards according to the selection mode.
3195 static pos sel_spread_half(pos p, int dir)
3197 unsigned long *ldata;
3199 int topy = -count234(scrollback);
3201 ldata = lineptr(p.y);
3206 * In this mode, every character is a separate unit, except
3207 * for runs of spaces at the end of a non-wrapping line.
3209 if (!(ldata[cols] & LATTR_WRAPPED)) {
3210 unsigned long *q = ldata + cols;
3211 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3213 if (q == ldata + cols)
3215 if (p.x >= q - ldata)
3216 p.x = (dir == -1 ? q - ldata : cols - 1);
3221 * In this mode, the units are maximal runs of characters
3222 * whose `wordness' has the same value.
3224 wvalue = wordtype(ldata[p.x]);
3228 if (wordtype(ldata[p.x + 1]) == wvalue)
3233 if (ldata[cols] & LATTR_WRAPPED) {
3234 unsigned long *ldata2;
3235 ldata2 = lineptr(p.y+1);
3236 if (wordtype(ldata2[0]) == wvalue) {
3249 if (wordtype(ldata[p.x - 1]) == wvalue)
3254 unsigned long *ldata2;
3257 ldata2 = lineptr(p.y-1);
3258 if ((ldata2[cols] & LATTR_WRAPPED) &&
3259 wordtype(ldata2[cols-1]) == wvalue) {
3271 * In this mode, every line is a unit.
3273 p.x = (dir == -1 ? 0 : cols - 1);
3279 static void sel_spread(void)
3281 if (seltype == LEXICOGRAPHIC) {
3282 selstart = sel_spread_half(selstart, -1);
3284 selend = sel_spread_half(selend, +1);
3289 void term_do_paste(void)
3294 get_clip(&data, &len);
3299 sfree(paste_buffer);
3300 paste_pos = paste_hold = paste_len = 0;
3301 paste_buffer = smalloc(len * sizeof(wchar_t));
3304 while (p < data + len) {
3305 while (p < data + len &&
3306 !(p <= data + len - sel_nl_sz &&
3307 !memcmp(p, sel_nl, sizeof(sel_nl))))
3312 for (i = 0; i < p - q; i++) {
3313 paste_buffer[paste_len++] = q[i];
3317 if (p <= data + len - sel_nl_sz &&
3318 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3319 paste_buffer[paste_len++] = '\r';
3325 /* Assume a small paste will be OK in one go. */
3326 if (paste_len < 256) {
3327 luni_send(paste_buffer, paste_len, 0);
3329 sfree(paste_buffer);
3331 paste_pos = paste_hold = paste_len = 0;
3334 get_clip(NULL, NULL);
3337 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3338 int shift, int ctrl, int alt)
3341 unsigned long *ldata;
3342 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3343 int default_seltype;
3347 if (a == MA_DRAG && !raw_mouse)
3352 if (a == MA_DRAG && !raw_mouse)
3365 selpoint.y = y + disptop;
3367 ldata = lineptr(selpoint.y);
3368 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3372 int encstate = 0, r, c;
3374 static int is_down = 0;
3378 encstate = 0x20; /* left button down */
3389 case MBT_WHEEL_DOWN:
3392 default: break; /* placate gcc warning about enum use */
3396 if (xterm_mouse == 1)
3409 default: break; /* placate gcc warning about enum use */
3418 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3419 ldisc_send(abuf, 6, 0);
3423 b = translate_button(b);
3426 * Set the selection type (rectangular or normal) at the start
3427 * of a selection attempt, from the state of Alt.
3429 if (!alt ^ !cfg.rect_select)
3430 default_seltype = RECTANGULAR;
3432 default_seltype = LEXICOGRAPHIC;
3434 if (selstate == NO_SELECTION) {
3435 seltype = default_seltype;
3438 if (b == MBT_SELECT && a == MA_CLICK) {
3440 selstate = ABOUT_TO;
3441 seltype = default_seltype;
3442 selanchor = selpoint;
3444 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3446 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3447 selstate = DRAGGING;
3448 selstart = selanchor = selpoint;
3452 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3453 (b == MBT_EXTEND && a != MA_RELEASE)) {
3454 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3456 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3457 if (seltype == LEXICOGRAPHIC) {
3459 * For normal selection, we extend by moving
3460 * whichever end of the current selection is closer
3463 if (posdiff(selpoint, selstart) <
3464 posdiff(selend, selstart) / 2) {
3468 selanchor = selstart;
3472 * For rectangular selection, we have a choice of
3473 * _four_ places to put selanchor and selpoint: the
3474 * four corners of the selection.
3476 if (2*selpoint.x < selstart.x + selend.x)
3477 selanchor.x = selend.x-1;
3479 selanchor.x = selstart.x;
3481 if (2*selpoint.y < selstart.y + selend.y)
3482 selanchor.y = selend.y;
3484 selanchor.y = selstart.y;
3486 selstate = DRAGGING;
3488 if (selstate != ABOUT_TO && selstate != DRAGGING)
3489 selanchor = selpoint;
3490 selstate = DRAGGING;
3491 if (seltype == LEXICOGRAPHIC) {
3493 * For normal selection, we set (selstart,selend) to
3494 * (selpoint,selanchor) in some order.
3496 if (poslt(selpoint, selanchor)) {
3497 selstart = selpoint;
3501 selstart = selanchor;
3507 * For rectangular selection, we may need to
3508 * interchange x and y coordinates (if the user has
3509 * dragged in the -x and +y directions, or vice versa).
3511 selstart.x = min(selanchor.x, selpoint.x);
3512 selend.x = 1+max(selanchor.x, selpoint.x);
3513 selstart.y = min(selanchor.y, selpoint.y);
3514 selend.y = max(selanchor.y, selpoint.y);
3517 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3518 if (selstate == DRAGGING) {
3520 * We've completed a selection. We now transfer the
3521 * data to the clipboard.
3523 clipme(selstart, selend, (seltype == RECTANGULAR));
3524 selstate = SELECTED;
3526 selstate = NO_SELECTION;
3527 } else if (b == MBT_PASTE
3528 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3539 sfree(paste_buffer);
3546 static long last_paste = 0;
3547 long now, paste_diff;
3552 /* Don't wait forever to paste */
3554 now = GetTickCount();
3555 paste_diff = now - last_paste;
3556 if (paste_diff >= 0 && paste_diff < 450)
3561 while (paste_pos < paste_len) {
3563 while (n + paste_pos < paste_len) {
3564 if (paste_buffer[paste_pos + n++] == '\r')
3567 luni_send(paste_buffer + paste_pos, n, 0);
3570 if (paste_pos < paste_len) {
3575 sfree(paste_buffer);
3580 static void deselect(void)
3582 selstate = NO_SELECTION;
3583 selstart.x = selstart.y = selend.x = selend.y = 0;
3586 void term_deselect(void)
3592 int term_ldisc(int option)
3594 if (option == LD_ECHO)
3595 return term_echoing;
3596 if (option == LD_EDIT)
3597 return term_editing;
3602 * from_backend(), to get data from the backend for the terminal.
3604 int from_backend(int is_stderr, char *data, int len)
3608 bufchain_add(&inbuf, data, len);
3611 * term_out() always completely empties inbuf. Therefore,
3612 * there's no reason at all to return anything other than zero
3613 * from this function, because there _can't_ be a question of
3614 * the remote side needing to wait until term_out() has cleared
3617 * This is a slightly suboptimal way to deal with SSH2 - in
3618 * principle, the window mechanism would allow us to continue
3619 * to accept data on forwarded ports and X connections even
3620 * while the terminal processing was going slowly - but we
3621 * can't do the 100% right thing without moving the terminal
3622 * processing into a separate thread, and that might hurt
3623 * portability. So we manage stdout buffering the old SSH1 way:
3624 * if the terminal processing goes slowly, the whole SSH
3625 * connection stops accepting data until it's ready.
3627 * In practice, I can't imagine this causing serious trouble.