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, save_wnext; /* 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 * When the user reconfigures us, we need to check the forbidden-
355 * alternate-screen config option, and also disable raw mouse mode
356 * if the user has disabled mouse reporting.
358 void term_reconfig(void)
360 if (cfg.no_alt_screen)
362 if (cfg.no_mouse_rep) {
364 set_raw_mouse_mode(0);
366 if (cfg.no_remote_charset) {
367 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
368 sco_acs = alt_sco_acs = 0;
374 * Clear the scrollback.
376 void term_clrsb(void)
380 while ((line = delpos234(scrollback, 0)) != NULL) {
387 * Initialise the terminal.
391 screen = alt_screen = scrollback = NULL;
393 disptext = dispcurs = NULL;
398 beephead = beeptail = NULL;
401 beep_overloaded = FALSE;
405 * Set up the terminal for a given size.
407 void term_size(int newrows, int newcols, int newsavelines)
410 unsigned long *newdisp, *line;
413 int save_alt_which = alt_which;
415 if (newrows == rows && newcols == cols && newsavelines == savelines)
416 return; /* nothing to do */
422 alt_b = marg_b = newrows - 1;
425 scrollback = newtree234(NULL);
426 screen = newtree234(NULL);
431 * Resize the screen and scrollback. We only need to shift
432 * lines around within our data structures, because lineptr()
433 * will take care of resizing each individual line if
436 * - If the new screen and the old screen differ in length, we
437 * must shunt some lines in from the scrollback or out to
440 * - If doing that fails to provide us with enough material to
441 * fill the new screen (i.e. the number of rows needed in
442 * the new screen exceeds the total number in the previous
443 * screen+scrollback), we must invent some blank lines to
446 * - Then, if the new scrollback length is less than the
447 * amount of scrollback we actually have, we must throw some
450 sblen = count234(scrollback);
451 /* Do this loop to expand the screen if newrows > rows */
452 for (i = rows; i < newrows; i++) {
454 line = delpos234(scrollback, --sblen);
456 line = smalloc(TSIZE * (newcols + 2));
458 for (j = 0; j <= newcols; j++)
459 line[j + 1] = ERASE_CHAR;
461 addpos234(screen, line, 0);
463 /* Do this loop to shrink the screen if newrows < rows */
464 for (i = newrows; i < rows; i++) {
465 line = delpos234(screen, 0);
466 addpos234(scrollback, line, sblen++);
468 assert(count234(screen) == newrows);
469 while (sblen > newsavelines) {
470 line = delpos234(scrollback, 0);
474 assert(count234(scrollback) <= newsavelines);
477 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
478 for (i = 0; i < newrows * (newcols + 1); i++)
479 newdisp[i] = ATTR_INVALID;
484 newalt = newtree234(NULL);
485 for (i = 0; i < newrows; i++) {
486 line = smalloc(TSIZE * (newcols + 2));
488 for (j = 0; j <= newcols; j++)
489 line[j + 1] = erase_char;
490 addpos234(newalt, line, i);
493 while (NULL != (line = delpos234(alt_screen, 0)))
495 freetree234(alt_screen);
499 tabs = srealloc(tabs, newcols * sizeof(*tabs));
502 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
503 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
507 curs.y += newrows - rows;
510 if (curs.y >= newrows)
511 curs.y = newrows - 1;
512 if (curs.x >= newcols)
513 curs.x = newcols - 1;
515 wrapnext = alt_wnext = FALSE;
519 savelines = newsavelines;
522 swap_screen(save_alt_which);
532 static void swap_screen(int which)
537 if (which == alt_which)
564 wrapnext = alt_wnext;
576 sco_acs = alt_sco_acs;
583 * Update the scroll bar.
585 static void update_sbar(void)
589 nscroll = count234(scrollback);
591 set_sbar(nscroll + rows, nscroll + disptop, rows);
595 * Check whether the region bounded by the two pointers intersects
596 * the scroll region, and de-select the on-screen selection if so.
598 static void check_selection(pos from, pos to)
600 if (poslt(from, selend) && poslt(selstart, to))
605 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
606 * for backward.) `sb' is TRUE if the scrolling is permitted to
607 * affect the scrollback buffer.
609 * NB this function invalidates all pointers into lines of the
610 * screen data structures. In particular, you MUST call fix_cpos
611 * after calling scroll() and before doing anything else that
612 * uses the cpos shortcut pointer.
614 static void scroll(int topline, int botline, int lines, int sb)
616 unsigned long *line, *line2;
619 if (topline != 0 || alt_which != 0)
624 line = delpos234(screen, botline);
625 line = resizeline(line, cols);
626 for (i = 0; i < cols; i++)
627 line[i + 1] = erase_char;
629 addpos234(screen, line, topline);
631 if (selstart.y >= topline && selstart.y <= botline) {
633 if (selstart.y > botline) {
634 selstart.y = botline;
638 if (selend.y >= topline && selend.y <= botline) {
640 if (selend.y > botline) {
650 line = delpos234(screen, topline);
651 if (sb && savelines > 0) {
652 int sblen = count234(scrollback);
654 * We must add this line to the scrollback. We'll
655 * remove a line from the top of the scrollback to
656 * replace it, or allocate a new one if the
657 * scrollback isn't full.
659 if (sblen == savelines) {
660 sblen--, line2 = delpos234(scrollback, 0);
662 line2 = smalloc(TSIZE * (cols + 2));
665 addpos234(scrollback, line, sblen);
669 * If the user is currently looking at part of the
670 * scrollback, and they haven't enabled any options
671 * that are going to reset the scrollback as a
672 * result of this movement, then the chances are
673 * they'd like to keep looking at the same line. So
674 * we move their viewpoint at the same rate as the
675 * scroll, at least until their viewpoint hits the
676 * top end of the scrollback buffer, at which point
677 * we don't have the choice any more.
679 * Thanks to Jan Holmen Holsten for the idea and
680 * initial implementation.
682 if (disptop > -savelines && disptop < 0)
685 line = resizeline(line, cols);
686 for (i = 0; i < cols; i++)
687 line[i + 1] = erase_char;
689 addpos234(screen, line, botline);
692 * If the selection endpoints move into the scrollback,
693 * we keep them moving until they hit the top. However,
694 * of course, if the line _hasn't_ moved into the
695 * scrollback then we don't do this, and cut them off
696 * at the top of the scroll region.
698 * This applies to selstart and selend (for an existing
699 * selection), and also selanchor (for one being
700 * selected as we speak).
702 seltop = sb ? -savelines : topline;
704 if (selstart.y >= seltop && selstart.y <= botline) {
706 if (selstart.y < seltop) {
711 if (selend.y >= seltop && selend.y <= botline) {
713 if (selend.y < seltop) {
718 if (selanchor.y >= seltop && selanchor.y <= botline) {
720 if (selanchor.y < seltop) {
721 selanchor.y = seltop;
732 * Move the cursor to a given position, clipping at boundaries. We
733 * may or may not want to clip at the scroll margin: marg_clip is 0
734 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
735 * even _being_ outside the margins.
737 static void move(int x, int y, int marg_clip)
744 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
746 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
760 * Save or restore the cursor and SGR mode.
762 static void save_cursor(int save)
766 save_attr = curr_attr;
769 save_wnext = wrapnext;
770 save_csattr = cset_attr[cset];
771 save_sco_acs = sco_acs;
774 /* Make sure the window hasn't shrunk since the save */
780 curr_attr = save_attr;
783 wrapnext = save_wnext;
785 * wrapnext might reset to False if the x position is no
786 * longer at the rightmost edge.
788 if (wrapnext && curs.x < cols-1)
790 cset_attr[cset] = save_csattr;
791 sco_acs = save_sco_acs;
794 erase_char = (' ' | ATTR_ASCII |
795 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
800 * Erase a large portion of the screen: the whole screen, or the
801 * whole line, or parts thereof.
803 static void erase_lots(int line_only, int from_begin, int to_end)
807 unsigned long *ldata;
829 check_selection(start, end);
831 /* Clear screen also forces a full window redraw, just in case. */
832 if (start.y == 0 && start.x == 0 && end.y == rows)
835 ldata = lineptr(start.y);
836 while (poslt(start, end)) {
837 if (start.x == cols && !erase_lattr)
838 ldata[start.x] &= ~LATTR_WRAPPED;
840 ldata[start.x] = erase_char;
841 if (incpos(start) && start.y < rows)
842 ldata = lineptr(start.y);
847 * Insert or delete characters within the current line. n is +ve if
848 * insertion is desired, and -ve for deletion.
850 static void insch(int n)
852 int dir = (n < 0 ? -1 : +1);
855 unsigned long *ldata;
857 n = (n < 0 ? -n : n);
858 if (n > cols - curs.x)
860 m = cols - curs.x - n;
862 cursplus.x = curs.x + n;
863 check_selection(curs, cursplus);
864 ldata = lineptr(curs.y);
866 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
868 ldata[curs.x + m++] = erase_char;
870 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
872 ldata[curs.x + n] = erase_char;
877 * Toggle terminal mode `mode' to state `state'. (`query' indicates
878 * whether the mode is a DEC private one or a normal one.)
880 static void toggle_mode(int mode, int query, int state)
886 case 1: /* application cursor keys */
887 app_cursor_keys = state;
889 case 2: /* VT52 mode */
892 blink_is_real = FALSE;
895 blink_is_real = cfg.blinktext;
898 case 3: /* 80/132 columns */
900 if (!cfg.no_remote_resize)
901 request_resize(state ? 132 : 80, rows);
904 case 5: /* reverse video */
906 * Toggle reverse video. If we receive an OFF within the
907 * visual bell timeout period after an ON, we trigger an
908 * effective visual bell, so that ESC[?5hESC[?5l will
909 * always be an actually _visible_ visual bell.
911 ticks = GetTickCount();
912 /* turn off a previous vbell to avoid inconsistencies */
913 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
915 if (rvideo && !state && /* we're turning it off... */
916 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
917 /* If there's no vbell timeout already, or this one lasts
918 * longer, replace vbell_timeout with ours. */
920 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
921 vbell_startpoint = rvbell_startpoint;
922 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
923 } else if (!rvideo && state) {
924 /* This is an ON, so we notice the time and save it. */
925 rvbell_startpoint = ticks;
928 seen_disp_event = TRUE;
932 case 6: /* DEC origin mode */
935 case 7: /* auto wrap */
938 case 8: /* auto key repeat */
941 case 10: /* set local edit mode */
942 term_editing = state;
943 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
945 case 25: /* enable/disable cursor */
946 compatibility2(OTHER, VT220);
948 seen_disp_event = TRUE;
950 case 47: /* alternate screen */
951 compatibility(OTHER);
953 swap_screen(cfg.no_alt_screen ? 0 : state);
956 case 1000: /* xterm mouse 1 */
957 xterm_mouse = state ? 1 : 0;
958 set_raw_mouse_mode(state);
960 case 1002: /* xterm mouse 2 */
961 xterm_mouse = state ? 2 : 0;
962 set_raw_mouse_mode(state);
966 case 4: /* set insert mode */
967 compatibility(VT102);
970 case 12: /* set echo mode */
971 term_echoing = !state;
972 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
974 case 20: /* Return sends ... */
975 cr_lf_return = state;
977 case 34: /* Make cursor BIG */
978 compatibility2(OTHER, VT220);
984 * Process an OSC sequence: set window title or icon name.
986 static void do_osc(void)
990 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
992 osc_string[osc_strlen] = '\0';
993 switch (esc_args[0]) {
996 if (!cfg.no_remote_wintitle)
997 set_icon(osc_string);
998 if (esc_args[0] == 1)
1000 /* fall through: parameter 0 means set both */
1003 if (!cfg.no_remote_wintitle)
1004 set_title(osc_string);
1011 * Remove everything currently in `inbuf' and stick it up on the
1012 * in-memory display. There's a big state machine in here to
1013 * process escape sequences...
1018 unsigned char localbuf[256], *chars;
1023 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1027 bufchain_prefix(&inbuf, &ret, &nchars);
1028 if (nchars > sizeof(localbuf))
1029 nchars = sizeof(localbuf);
1030 memcpy(localbuf, ret, nchars);
1031 bufchain_consume(&inbuf, nchars);
1033 assert(chars != NULL);
1039 * Optionally log the session traffic to a file. Useful for
1040 * debugging and possibly also useful for actual logging.
1042 if (cfg.logtype == LGTYP_DEBUG)
1043 logtraffic((unsigned char) c, LGTYP_DEBUG);
1049 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1050 * be able to display 8-bit characters, but I'll let that go 'cause
1054 /* First see about all those translations. */
1055 if (termstate == TOPLEVEL) {
1057 switch (utf_state) {
1060 /* UTF-8 must be stateless so we ignore iso2022. */
1061 if (unitab_ctrl[c] != 0xFF)
1063 else c = ((unsigned char)c) | ATTR_ASCII;
1065 } else if ((c & 0xe0) == 0xc0) {
1066 utf_size = utf_state = 1;
1067 utf_char = (c & 0x1f);
1068 } else if ((c & 0xf0) == 0xe0) {
1069 utf_size = utf_state = 2;
1070 utf_char = (c & 0x0f);
1071 } else if ((c & 0xf8) == 0xf0) {
1072 utf_size = utf_state = 3;
1073 utf_char = (c & 0x07);
1074 } else if ((c & 0xfc) == 0xf8) {
1075 utf_size = utf_state = 4;
1076 utf_char = (c & 0x03);
1077 } else if ((c & 0xfe) == 0xfc) {
1078 utf_size = utf_state = 5;
1079 utf_char = (c & 0x01);
1090 if ((c & 0xC0) != 0x80) {
1096 utf_char = (utf_char << 6) | (c & 0x3f);
1102 /* Is somebody trying to be evil! */
1104 (c < 0x800 && utf_size >= 2) ||
1105 (c < 0x10000 && utf_size >= 3) ||
1106 (c < 0x200000 && utf_size >= 4) ||
1107 (c < 0x4000000 && utf_size >= 5))
1110 /* Unicode line separator and paragraph separator are CR-LF */
1111 if (c == 0x2028 || c == 0x2029)
1114 /* High controls are probably a Baaad idea too. */
1118 /* The UTF-16 surrogates are not nice either. */
1119 /* The standard give the option of decoding these:
1120 * I don't want to! */
1121 if (c >= 0xD800 && c < 0xE000)
1124 /* ISO 10646 characters now limited to UTF-16 range. */
1128 /* This is currently a TagPhobic application.. */
1129 if (c >= 0xE0000 && c <= 0xE007F)
1132 /* U+FEFF is best seen as a null. */
1135 /* But U+FFFE is an error. */
1136 if (c == 0xFFFE || c == 0xFFFF)
1139 /* Oops this is a 16bit implementation */
1144 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1146 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1148 if (sco_acs == 2) c ^= 0x80;
1151 switch (cset_attr[cset]) {
1153 * Linedraw characters are different from 'ESC ( B'
1154 * only for a small range. For ones outside that
1155 * range, make sure we use the same font as well as
1156 * the same encoding.
1159 if (unitab_ctrl[c] != 0xFF)
1162 c = ((unsigned char) c) | ATTR_LINEDRW;
1166 /* If UK-ASCII, make the '#' a LineDraw Pound */
1168 c = '}' | ATTR_LINEDRW;
1171 /*FALLTHROUGH*/ case ATTR_ASCII:
1172 if (unitab_ctrl[c] != 0xFF)
1175 c = ((unsigned char) c) | ATTR_ASCII;
1178 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1184 /* How about C1 controls ? */
1185 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1186 has_compat(VT220)) {
1187 termstate = SEEN_ESC;
1189 c = '@' + (c & 0x1F);
1192 /* Or the GL control. */
1193 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1194 if (curs.x && !wrapnext)
1198 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1199 *cpos = (' ' | curr_attr | ATTR_ASCII);
1201 /* Or normal C0 controls. */
1202 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1204 case '\005': /* terminal type query */
1205 /* Strictly speaking this is VT100 but a VT100 defaults to
1206 * no response. Other terminals respond at their option.
1208 * Don't put a CR in the default string as this tends to
1209 * upset some weird software.
1211 * An xterm returns "xterm" (5 characters)
1213 compatibility(ANSIMIN);
1215 char abuf[256], *s, *d;
1217 for (s = cfg.answerback, d = abuf; *s; s++) {
1219 if (*s >= 'a' && *s <= 'z')
1220 *d++ = (*s - ('a' - 1));
1221 else if ((*s >= '@' && *s <= '_') ||
1222 *s == '?' || (*s & 0x80))
1227 } else if (*s == '^') {
1232 lpage_send(CP_ACP, abuf, d - abuf, 0);
1237 struct beeptime *newbeep;
1238 unsigned long ticks;
1240 ticks = GetTickCount();
1242 if (!beep_overloaded) {
1243 newbeep = smalloc(sizeof(struct beeptime));
1244 newbeep->ticks = ticks;
1245 newbeep->next = NULL;
1249 beeptail->next = newbeep;
1255 * Throw out any beeps that happened more than
1259 beephead->ticks < ticks - cfg.bellovl_t) {
1260 struct beeptime *tmp = beephead;
1261 beephead = tmp->next;
1268 if (cfg.bellovl && beep_overloaded &&
1269 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1271 * If we're currently overloaded and the
1272 * last beep was more than s seconds ago,
1273 * leave overload mode.
1275 beep_overloaded = FALSE;
1276 } else if (cfg.bellovl && !beep_overloaded &&
1277 nbeeps >= cfg.bellovl_n) {
1279 * Now, if we have n or more beeps
1280 * remaining in the queue, go into overload
1283 beep_overloaded = TRUE;
1288 * Perform an actual beep if we're not overloaded.
1290 if (!cfg.bellovl || !beep_overloaded) {
1292 if (cfg.beep == BELL_VISUAL) {
1294 vbell_startpoint = ticks;
1302 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1303 else if (curs.x == 0 && curs.y > 0)
1304 curs.x = cols - 1, curs.y--;
1310 seen_disp_event = TRUE;
1313 compatibility(VT100);
1317 compatibility(VT100);
1322 termstate = VT52_ESC;
1324 compatibility(ANSIMIN);
1325 termstate = SEEN_ESC;
1333 seen_disp_event = TRUE;
1335 logtraffic((unsigned char) c, LGTYP_ASCII);
1338 if (has_compat(SCOANSI)) {
1340 erase_lots(FALSE, FALSE, TRUE);
1343 seen_disp_event = 1;
1347 compatibility(VT100);
1349 if (curs.y == marg_b)
1350 scroll(marg_t, marg_b, 1, TRUE);
1351 else if (curs.y < rows - 1)
1357 seen_disp_event = 1;
1359 logtraffic((unsigned char) c, LGTYP_ASCII);
1363 pos old_curs = curs;
1364 unsigned long *ldata = lineptr(curs.y);
1368 } while (curs.x < cols - 1 && !tabs[curs.x]);
1370 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1371 if (curs.x >= cols / 2)
1372 curs.x = cols / 2 - 1;
1379 check_selection(old_curs, curs);
1381 seen_disp_event = TRUE;
1385 switch (termstate) {
1387 /* Only graphic characters get this far, ctrls are stripped above */
1388 if (wrapnext && wrap) {
1389 cpos[1] |= LATTR_WRAPPED;
1390 if (curs.y == marg_b)
1391 scroll(marg_t, marg_b, 1, TRUE);
1392 else if (curs.y < rows - 1)
1400 if (selstate != NO_SELECTION) {
1401 pos cursplus = curs;
1403 check_selection(curs, cursplus);
1405 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1406 logtraffic((unsigned char) c, LGTYP_ASCII);
1408 extern int wcwidth(wchar_t ucs);
1413 width = wcwidth((wchar_t) c);
1416 *cpos++ = c | curr_attr;
1417 if (++curs.x == cols) {
1418 *cpos |= LATTR_WRAPPED;
1419 if (curs.y == marg_b)
1420 scroll(marg_t, marg_b, 1, TRUE);
1421 else if (curs.y < rows - 1)
1426 *cpos++ = UCSWIDE | curr_attr;
1429 *cpos++ = c | curr_attr;
1436 if (curs.x == cols) {
1440 if (wrap && vt52_mode) {
1441 cpos[1] |= LATTR_WRAPPED;
1442 if (curs.y == marg_b)
1443 scroll(marg_t, marg_b, 1, TRUE);
1444 else if (curs.y < rows - 1)
1451 seen_disp_event = 1;
1456 * This state is virtually identical to SEEN_ESC, with the
1457 * exception that we have an OSC sequence in the pipeline,
1458 * and _if_ we see a backslash, we process it.
1462 termstate = TOPLEVEL;
1465 /* else fall through */
1467 if (c >= ' ' && c <= '/') {
1474 termstate = TOPLEVEL;
1475 switch (ANSI(c, esc_query)) {
1476 case '[': /* enter CSI mode */
1477 termstate = SEEN_CSI;
1479 esc_args[0] = ARG_DEFAULT;
1482 case ']': /* xterm escape sequences */
1483 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1484 compatibility(OTHER);
1485 termstate = SEEN_OSC;
1488 case '7': /* save cursor */
1489 compatibility(VT100);
1492 case '8': /* restore cursor */
1493 compatibility(VT100);
1495 seen_disp_event = TRUE;
1498 compatibility(VT100);
1499 app_keypad_keys = TRUE;
1502 compatibility(VT100);
1503 app_keypad_keys = FALSE;
1505 case 'D': /* exactly equivalent to LF */
1506 compatibility(VT100);
1507 if (curs.y == marg_b)
1508 scroll(marg_t, marg_b, 1, TRUE);
1509 else if (curs.y < rows - 1)
1513 seen_disp_event = TRUE;
1515 case 'E': /* exactly equivalent to CR-LF */
1516 compatibility(VT100);
1518 if (curs.y == marg_b)
1519 scroll(marg_t, marg_b, 1, TRUE);
1520 else if (curs.y < rows - 1)
1524 seen_disp_event = TRUE;
1526 case 'M': /* reverse index - backwards LF */
1527 compatibility(VT100);
1528 if (curs.y == marg_t)
1529 scroll(marg_t, marg_b, -1, TRUE);
1530 else if (curs.y > 0)
1534 seen_disp_event = TRUE;
1536 case 'Z': /* terminal type query */
1537 compatibility(VT100);
1538 ldisc_send(id_string, strlen(id_string), 0);
1540 case 'c': /* restore power-on settings */
1541 compatibility(VT100);
1544 if (!cfg.no_remote_resize)
1545 request_resize(80, rows);
1550 seen_disp_event = TRUE;
1552 case 'H': /* set a tab */
1553 compatibility(VT100);
1554 tabs[curs.x] = TRUE;
1557 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1558 compatibility(VT100);
1560 unsigned long *ldata;
1564 for (i = 0; i < rows; i++) {
1566 for (j = 0; j < cols; j++)
1567 ldata[j] = ATTR_DEFAULT | 'E';
1571 seen_disp_event = TRUE;
1572 scrtop.x = scrtop.y = 0;
1575 check_selection(scrtop, scrbot);
1579 case ANSI('3', '#'):
1580 case ANSI('4', '#'):
1581 case ANSI('5', '#'):
1582 case ANSI('6', '#'):
1583 compatibility(VT100);
1585 unsigned long nlattr;
1586 unsigned long *ldata;
1587 switch (ANSI(c, esc_query)) {
1588 case ANSI('3', '#'):
1591 case ANSI('4', '#'):
1594 case ANSI('5', '#'):
1595 nlattr = LATTR_NORM;
1597 default: /* spiritually case ANSI('6', '#'): */
1598 nlattr = LATTR_WIDE;
1601 ldata = lineptr(curs.y);
1602 ldata[cols] &= ~LATTR_MODE;
1603 ldata[cols] |= nlattr;
1607 case ANSI('A', '('):
1608 compatibility(VT100);
1609 if (!cfg.no_remote_charset)
1610 cset_attr[0] = ATTR_GBCHR;
1612 case ANSI('B', '('):
1613 compatibility(VT100);
1614 if (!cfg.no_remote_charset)
1615 cset_attr[0] = ATTR_ASCII;
1617 case ANSI('0', '('):
1618 compatibility(VT100);
1619 if (!cfg.no_remote_charset)
1620 cset_attr[0] = ATTR_LINEDRW;
1622 case ANSI('U', '('):
1623 compatibility(OTHER);
1624 if (!cfg.no_remote_charset)
1625 cset_attr[0] = ATTR_SCOACS;
1628 case ANSI('A', ')'):
1629 compatibility(VT100);
1630 if (!cfg.no_remote_charset)
1631 cset_attr[1] = ATTR_GBCHR;
1633 case ANSI('B', ')'):
1634 compatibility(VT100);
1635 if (!cfg.no_remote_charset)
1636 cset_attr[1] = ATTR_ASCII;
1638 case ANSI('0', ')'):
1639 compatibility(VT100);
1640 if (!cfg.no_remote_charset)
1641 cset_attr[1] = ATTR_LINEDRW;
1643 case ANSI('U', ')'):
1644 compatibility(OTHER);
1645 if (!cfg.no_remote_charset)
1646 cset_attr[1] = ATTR_SCOACS;
1649 case ANSI('8', '%'): /* Old Linux code */
1650 case ANSI('G', '%'):
1651 compatibility(OTHER);
1652 if (!cfg.no_remote_charset)
1655 case ANSI('@', '%'):
1656 compatibility(OTHER);
1657 if (!cfg.no_remote_charset)
1663 termstate = TOPLEVEL; /* default */
1665 if (esc_nargs <= ARGS_MAX) {
1666 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1667 esc_args[esc_nargs - 1] = 0;
1668 esc_args[esc_nargs - 1] =
1669 10 * esc_args[esc_nargs - 1] + c - '0';
1671 termstate = SEEN_CSI;
1672 } else if (c == ';') {
1673 if (++esc_nargs <= ARGS_MAX)
1674 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1675 termstate = SEEN_CSI;
1676 } else if (c < '@') {
1683 termstate = SEEN_CSI;
1685 switch (ANSI(c, esc_query)) {
1686 case 'A': /* move up N lines */
1687 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1688 seen_disp_event = TRUE;
1690 case 'e': /* move down N lines */
1691 compatibility(ANSI);
1694 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1695 seen_disp_event = TRUE;
1697 case ANSI('c', '>'): /* report xterm version */
1698 compatibility(OTHER);
1699 /* this reports xterm version 136 so that VIM can
1700 use the drag messages from the mouse reporting */
1701 ldisc_send("\033[>0;136;0c", 11, 0);
1703 case 'a': /* move right N cols */
1704 compatibility(ANSI);
1707 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1708 seen_disp_event = TRUE;
1710 case 'D': /* move left N cols */
1711 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1712 seen_disp_event = TRUE;
1714 case 'E': /* move down N lines and CR */
1715 compatibility(ANSI);
1716 move(0, curs.y + def(esc_args[0], 1), 1);
1717 seen_disp_event = TRUE;
1719 case 'F': /* move up N lines and CR */
1720 compatibility(ANSI);
1721 move(0, curs.y - def(esc_args[0], 1), 1);
1722 seen_disp_event = TRUE;
1725 case '`': /* set horizontal posn */
1726 compatibility(ANSI);
1727 move(def(esc_args[0], 1) - 1, curs.y, 0);
1728 seen_disp_event = TRUE;
1730 case 'd': /* set vertical posn */
1731 compatibility(ANSI);
1733 (dec_om ? marg_t : 0) + def(esc_args[0],
1736 seen_disp_event = TRUE;
1739 case 'f': /* set horz and vert posns at once */
1741 esc_args[1] = ARG_DEFAULT;
1742 move(def(esc_args[1], 1) - 1,
1743 (dec_om ? marg_t : 0) + def(esc_args[0],
1746 seen_disp_event = TRUE;
1748 case 'J': /* erase screen or parts of it */
1750 unsigned int i = def(esc_args[0], 0) + 1;
1753 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1756 seen_disp_event = TRUE;
1758 case 'K': /* erase line or parts of it */
1760 unsigned int i = def(esc_args[0], 0) + 1;
1763 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1765 seen_disp_event = TRUE;
1767 case 'L': /* insert lines */
1768 compatibility(VT102);
1769 if (curs.y <= marg_b)
1770 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1773 seen_disp_event = TRUE;
1775 case 'M': /* delete lines */
1776 compatibility(VT102);
1777 if (curs.y <= marg_b)
1778 scroll(curs.y, marg_b, def(esc_args[0], 1),
1781 seen_disp_event = TRUE;
1783 case '@': /* insert chars */
1784 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1785 compatibility(VT102);
1786 insch(def(esc_args[0], 1));
1787 seen_disp_event = TRUE;
1789 case 'P': /* delete chars */
1790 compatibility(VT102);
1791 insch(-def(esc_args[0], 1));
1792 seen_disp_event = TRUE;
1794 case 'c': /* terminal type query */
1795 compatibility(VT100);
1796 /* This is the response for a VT102 */
1797 ldisc_send(id_string, strlen(id_string), 0);
1799 case 'n': /* cursor position query */
1800 if (esc_args[0] == 6) {
1802 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1804 ldisc_send(buf, strlen(buf), 0);
1805 } else if (esc_args[0] == 5) {
1806 ldisc_send("\033[0n", 4, 0);
1809 case 'h': /* toggle modes to high */
1811 compatibility(VT100);
1814 for (i = 0; i < esc_nargs; i++)
1815 toggle_mode(esc_args[i], esc_query, TRUE);
1818 case 'l': /* toggle modes to low */
1820 compatibility(VT100);
1823 for (i = 0; i < esc_nargs; i++)
1824 toggle_mode(esc_args[i], esc_query, FALSE);
1827 case 'g': /* clear tabs */
1828 compatibility(VT100);
1829 if (esc_nargs == 1) {
1830 if (esc_args[0] == 0) {
1831 tabs[curs.x] = FALSE;
1832 } else if (esc_args[0] == 3) {
1834 for (i = 0; i < cols; i++)
1839 case 'r': /* set scroll margins */
1840 compatibility(VT100);
1841 if (esc_nargs <= 2) {
1843 top = def(esc_args[0], 1) - 1;
1844 bot = (esc_nargs <= 1
1846 0 ? rows : def(esc_args[1], rows)) - 1;
1849 /* VTTEST Bug 9 - if region is less than 2 lines
1850 * don't change region.
1852 if (bot - top > 0) {
1857 * I used to think the cursor should be
1858 * placed at the top of the newly marginned
1859 * area. Apparently not: VMS TPU falls over
1862 * Well actually it should for Origin mode - RDB
1864 curs.y = (dec_om ? marg_t : 0);
1866 seen_disp_event = TRUE;
1870 case 'm': /* set graphics rendition */
1873 * A VT100 without the AVO only had one attribute, either
1874 * underline or reverse video depending on the cursor type,
1875 * this was selected by CSI 7m.
1878 * This is sometimes DIM, eg on the GIGI and Linux
1880 * This is sometimes INVIS various ANSI.
1882 * This like 22 disables BOLD, DIM and INVIS
1884 * The ANSI colours appear on any terminal that has colour
1885 * (obviously) but the interaction between sgr0 and the
1886 * colours varies but is usually related to the background
1887 * colour erase item.
1888 * The interaction between colour attributes and the mono
1889 * ones is also very implementation dependent.
1891 * The 39 and 49 attributes are likely to be unimplemented.
1894 for (i = 0; i < esc_nargs; i++) {
1895 switch (def(esc_args[i], 0)) {
1896 case 0: /* restore defaults */
1897 curr_attr = ATTR_DEFAULT;
1899 case 1: /* enable bold */
1900 compatibility(VT100AVO);
1901 curr_attr |= ATTR_BOLD;
1903 case 21: /* (enable double underline) */
1904 compatibility(OTHER);
1905 case 4: /* enable underline */
1906 compatibility(VT100AVO);
1907 curr_attr |= ATTR_UNDER;
1909 case 5: /* enable blink */
1910 compatibility(VT100AVO);
1911 curr_attr |= ATTR_BLINK;
1913 case 7: /* enable reverse video */
1914 curr_attr |= ATTR_REVERSE;
1916 case 10: /* SCO acs off */
1917 compatibility(SCOANSI);
1918 if (cfg.no_remote_charset) break;
1920 case 11: /* SCO acs on */
1921 compatibility(SCOANSI);
1922 if (cfg.no_remote_charset) break;
1924 case 12: /* SCO acs on flipped */
1925 compatibility(SCOANSI);
1926 if (cfg.no_remote_charset) break;
1928 case 22: /* disable bold */
1929 compatibility2(OTHER, VT220);
1930 curr_attr &= ~ATTR_BOLD;
1932 case 24: /* disable underline */
1933 compatibility2(OTHER, VT220);
1934 curr_attr &= ~ATTR_UNDER;
1936 case 25: /* disable blink */
1937 compatibility2(OTHER, VT220);
1938 curr_attr &= ~ATTR_BLINK;
1940 case 27: /* disable reverse video */
1941 compatibility2(OTHER, VT220);
1942 curr_attr &= ~ATTR_REVERSE;
1953 curr_attr &= ~ATTR_FGMASK;
1955 (esc_args[i] - 30) << ATTR_FGSHIFT;
1957 case 39: /* default-foreground */
1958 curr_attr &= ~ATTR_FGMASK;
1959 curr_attr |= ATTR_DEFFG;
1970 curr_attr &= ~ATTR_BGMASK;
1972 (esc_args[i] - 40) << ATTR_BGSHIFT;
1974 case 49: /* default-background */
1975 curr_attr &= ~ATTR_BGMASK;
1976 curr_attr |= ATTR_DEFBG;
1981 erase_char = (' ' | ATTR_ASCII |
1983 (ATTR_FGMASK | ATTR_BGMASK)));
1986 case 's': /* save cursor */
1989 case 'u': /* restore cursor */
1991 seen_disp_event = TRUE;
1993 case 't': /* set page size - ie window height */
1995 * VT340/VT420 sequence DECSLPP, DEC only allows values
1996 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1997 * illegal values (eg first arg 1..9) for window changing
2001 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2002 compatibility(VT340TEXT);
2003 if (!cfg.no_remote_resize)
2004 request_resize(cols, def(esc_args[0], 24));
2006 } else if (esc_nargs >= 1 &&
2009 compatibility(OTHER);
2011 switch (esc_args[0]) {
2021 if (esc_nargs >= 3) {
2022 if (!cfg.no_remote_resize)
2023 move_window(def(esc_args[1], 0),
2024 def(esc_args[2], 0));
2028 /* We should resize the window to a given
2029 * size in pixels here, but currently our
2030 * resizing code isn't healthy enough to
2034 set_zorder(TRUE); /* move to top */
2037 set_zorder(FALSE); /* move to bottom */
2043 if (esc_nargs >= 3) {
2044 if (!cfg.no_remote_resize)
2045 request_resize(def(esc_args[2], cfg.width),
2046 def(esc_args[1], cfg.height));
2051 set_zoomed(esc_args[1] ? TRUE : FALSE);
2054 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2058 get_window_pos(&x, &y);
2059 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2060 ldisc_send(buf, len, 0);
2063 get_window_pixels(&x, &y);
2064 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2065 ldisc_send(buf, len, 0);
2068 len = sprintf(buf, "\033[8;%d;%dt",
2070 ldisc_send(buf, len, 0);
2074 * Hmmm. Strictly speaking we
2075 * should return `the size of the
2076 * screen in characters', but
2077 * that's not easy: (a) window
2078 * furniture being what it is it's
2079 * hard to compute, and (b) in
2080 * resize-font mode maximising the
2081 * window wouldn't change the
2082 * number of characters. *shrug*. I
2083 * think we'll ignore it for the
2084 * moment and see if anyone
2085 * complains, and then ask them
2086 * what they would like it to do.
2090 p = get_window_title(TRUE);
2092 ldisc_send("\033]L", 3, 0);
2093 ldisc_send(p, len, 0);
2094 ldisc_send("\033\\", 2, 0);
2097 p = get_window_title(FALSE);
2099 ldisc_send("\033]l", 3, 0);
2100 ldisc_send(p, len, 0);
2101 ldisc_send("\033\\", 2, 0);
2107 compatibility(SCOANSI);
2108 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2111 seen_disp_event = TRUE;
2114 compatibility(SCOANSI);
2115 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2118 seen_disp_event = TRUE;
2120 case ANSI('|', '*'):
2121 /* VT420 sequence DECSNLS
2122 * Set number of lines on screen
2123 * VT420 uses VGA like hardware and can support any size in
2124 * reasonable range (24..49 AIUI) with no default specified.
2126 compatibility(VT420);
2127 if (esc_nargs == 1 && esc_args[0] > 0) {
2128 if (!cfg.no_remote_resize)
2129 request_resize(cols, def(esc_args[0], cfg.height));
2133 case ANSI('|', '$'):
2134 /* VT340/VT420 sequence DECSCPP
2135 * Set number of columns per page
2136 * Docs imply range is only 80 or 132, but I'll allow any.
2138 compatibility(VT340TEXT);
2139 if (esc_nargs <= 1) {
2140 if (!cfg.no_remote_resize)
2141 request_resize(def(esc_args[0], cfg.width), rows);
2145 case 'X': /* write N spaces w/o moving cursor */
2146 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2147 compatibility(ANSIMIN);
2149 int n = def(esc_args[0], 1);
2151 unsigned long *p = cpos;
2152 if (n > cols - curs.x)
2156 check_selection(curs, cursplus);
2159 seen_disp_event = TRUE;
2162 case 'x': /* report terminal characteristics */
2163 compatibility(VT100);
2166 int i = def(esc_args[0], 0);
2167 if (i == 0 || i == 1) {
2168 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2170 ldisc_send(buf, 20, 0);
2174 case 'Z': /* BackTab for xterm */
2175 compatibility(OTHER);
2177 int i = def(esc_args[0], 1);
2178 pos old_curs = curs;
2180 for(;i>0 && curs.x>0; i--) {
2183 } while (curs.x >0 && !tabs[curs.x]);
2186 check_selection(old_curs, curs);
2189 case ANSI('L', '='):
2190 compatibility(OTHER);
2191 use_bce = (esc_args[0] <= 0);
2192 erase_char = ERASE_CHAR;
2194 erase_char = (' ' | ATTR_ASCII |
2196 (ATTR_FGMASK | ATTR_BGMASK)));
2198 case ANSI('E', '='):
2199 compatibility(OTHER);
2200 blink_is_real = (esc_args[0] >= 1);
2202 case ANSI('p', '"'):
2203 /* Allow the host to make this emulator a 'perfect' VT102.
2204 * This first appeared in the VT220, but we do need to get
2205 * back to PuTTY mode so I won't check it.
2207 * The arg in 40..42,50 are a PuTTY extension.
2208 * The 2nd arg, 8bit vs 7bit is not checked.
2210 * Setting VT102 mode should also change the Fkeys to
2211 * generate PF* codes as a real VT102 has no Fkeys.
2212 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2215 * Note ESC c will NOT change this!
2218 switch (esc_args[0]) {
2220 compatibility_level &= ~TM_VTXXX;
2221 compatibility_level |= TM_VT102;
2224 compatibility_level &= ~TM_VTXXX;
2225 compatibility_level |= TM_VT220;
2229 if (esc_args[0] > 60 && esc_args[0] < 70)
2230 compatibility_level |= TM_VTXXX;
2234 compatibility_level &= TM_VTXXX;
2237 compatibility_level = TM_PUTTY;
2240 compatibility_level = TM_SCOANSI;
2244 compatibility_level = TM_PUTTY;
2250 /* Change the response to CSI c */
2251 if (esc_args[0] == 50) {
2254 strcpy(id_string, "\033[?");
2255 for (i = 1; i < esc_nargs; i++) {
2257 strcat(id_string, ";");
2258 sprintf(lbuf, "%d", esc_args[i]);
2259 strcat(id_string, lbuf);
2261 strcat(id_string, "c");
2264 /* Is this a good idea ?
2265 * Well we should do a soft reset at this point ...
2267 if (!has_compat(VT420) && has_compat(VT100)) {
2268 if (!cfg.no_remote_resize) {
2270 request_resize(132, 24);
2272 request_resize(80, 24);
2282 case 'P': /* Linux palette sequence */
2283 termstate = SEEN_OSC_P;
2286 case 'R': /* Linux palette reset */
2289 termstate = TOPLEVEL;
2291 case 'W': /* word-set */
2292 termstate = SEEN_OSC_W;
2305 esc_args[0] = 10 * esc_args[0] + c - '0';
2309 * Grotty hack to support xterm and DECterm title
2310 * sequences concurrently.
2312 if (esc_args[0] == 2) {
2316 /* else fall through */
2318 termstate = OSC_STRING;
2324 * This OSC stuff is EVIL. It takes just one character to get into
2325 * sysline mode and it's not initially obvious how to get out.
2326 * So I've added CR and LF as string aborts.
2327 * This shouldn't effect compatibility as I believe embedded
2328 * control characters are supposed to be interpreted (maybe?)
2329 * and they don't display anything useful anyway.
2333 if (c == '\n' || c == '\r') {
2334 termstate = TOPLEVEL;
2335 } else if (c == 0234 || c == '\007') {
2337 * These characters terminate the string; ST and BEL
2338 * terminate the sequence and trigger instant
2339 * processing of it, whereas ESC goes back to SEEN_ESC
2340 * mode unless it is followed by \, in which case it is
2341 * synonymous with ST in the first place.
2344 termstate = TOPLEVEL;
2345 } else if (c == '\033')
2346 termstate = OSC_MAYBE_ST;
2347 else if (osc_strlen < OSC_STR_MAX)
2348 osc_string[osc_strlen++] = c;
2352 int max = (osc_strlen == 0 ? 21 : 16);
2354 if (c >= '0' && c <= '9')
2356 else if (c >= 'A' && c <= 'A' + max - 10)
2358 else if (c >= 'a' && c <= 'a' + max - 10)
2361 termstate = TOPLEVEL;
2364 osc_string[osc_strlen++] = val;
2365 if (osc_strlen >= 7) {
2366 palette_set(osc_string[0],
2367 osc_string[1] * 16 + osc_string[2],
2368 osc_string[3] * 16 + osc_string[4],
2369 osc_string[5] * 16 + osc_string[6]);
2371 termstate = TOPLEVEL;
2387 esc_args[0] = 10 * esc_args[0] + c - '0';
2390 termstate = OSC_STRING;
2395 termstate = TOPLEVEL;
2396 seen_disp_event = TRUE;
2399 move(curs.x, curs.y - 1, 1);
2402 move(curs.x, curs.y + 1, 1);
2405 move(curs.x + 1, curs.y, 1);
2408 move(curs.x - 1, curs.y, 1);
2411 * From the VT100 Manual
2412 * NOTE: The special graphics characters in the VT100
2413 * are different from those in the VT52
2415 * From VT102 manual:
2416 * 137 _ Blank - Same
2417 * 140 ` Reserved - Humm.
2418 * 141 a Solid rectangle - Similar
2419 * 142 b 1/ - Top half of fraction for the
2420 * 143 c 3/ - subscript numbers below.
2423 * 146 f Degrees - Same
2424 * 147 g Plus or minus - Same
2426 * 151 i Ellipsis (dots)
2429 * 154 l Bar at scan 0
2430 * 155 m Bar at scan 1
2431 * 156 n Bar at scan 2
2432 * 157 o Bar at scan 3 - Similar
2433 * 160 p Bar at scan 4 - Similar
2434 * 161 q Bar at scan 5 - Similar
2435 * 162 r Bar at scan 6 - Same
2436 * 163 s Bar at scan 7 - Similar
2451 cset_attr[cset = 0] = ATTR_LINEDRW;
2454 cset_attr[cset = 0] = ATTR_ASCII;
2461 scroll(0, rows - 1, -1, TRUE);
2462 else if (curs.y > 0)
2468 erase_lots(FALSE, FALSE, TRUE);
2472 erase_lots(TRUE, FALSE, TRUE);
2476 /* XXX Print cursor line */
2479 /* XXX Start controller mode */
2482 /* XXX Stop controller mode */
2486 termstate = VT52_Y1;
2489 ldisc_send("\033/Z", 3, 0);
2492 app_keypad_keys = TRUE;
2495 app_keypad_keys = FALSE;
2498 /* XXX This should switch to VT100 mode not current or default
2499 * VT mode. But this will only have effect in a VT220+
2503 blink_is_real = cfg.blinktext;
2507 /* XXX Enter auto print mode */
2510 /* XXX Exit auto print mode */
2513 /* XXX Print screen */
2519 /* compatibility(ATARI) */
2521 erase_lots(FALSE, FALSE, TRUE);
2525 /* compatibility(ATARI) */
2526 if (curs.y <= marg_b)
2527 scroll(curs.y, marg_b, -1, FALSE);
2530 /* compatibility(ATARI) */
2531 if (curs.y <= marg_b)
2532 scroll(curs.y, marg_b, 1, TRUE);
2535 /* compatibility(ATARI) */
2536 termstate = VT52_FG;
2539 /* compatibility(ATARI) */
2540 termstate = VT52_BG;
2543 /* compatibility(ATARI) */
2544 erase_lots(FALSE, TRUE, FALSE);
2548 /* compatibility(ATARI) */
2552 /* compatibility(ATARI) */
2555 /* case 'j': Save cursor position - broken on ST */
2556 /* case 'k': Restore cursor position */
2558 /* compatibility(ATARI) */
2559 erase_lots(TRUE, TRUE, TRUE);
2565 /* compatibility(ATARI) */
2566 erase_lots(TRUE, TRUE, FALSE);
2569 /* compatibility(ATARI) */
2570 curr_attr |= ATTR_REVERSE;
2573 /* compatibility(ATARI) */
2574 curr_attr &= ~ATTR_REVERSE;
2576 case 'v': /* wrap Autowrap on - Wyse style */
2577 /* compatibility(ATARI) */
2580 case 'w': /* Autowrap off */
2581 /* compatibility(ATARI) */
2586 /* compatibility(OTHER) */
2588 curr_attr = ATTR_DEFAULT;
2590 erase_char = (' ' | ATTR_ASCII |
2592 (ATTR_FGMASK | ATTR_BGMASK)));
2595 /* compatibility(VI50) */
2596 curr_attr |= ATTR_UNDER;
2599 /* compatibility(VI50) */
2600 curr_attr &= ~ATTR_UNDER;
2603 /* compatibility(VI50) */
2605 curr_attr |= ATTR_BOLD;
2608 /* compatibility(VI50) */
2610 curr_attr &= ~ATTR_BOLD;
2616 termstate = VT52_Y2;
2617 move(curs.x, c - ' ', 0);
2620 termstate = TOPLEVEL;
2621 move(c - ' ', curs.y, 0);
2626 termstate = TOPLEVEL;
2627 curr_attr &= ~ATTR_FGMASK;
2628 curr_attr &= ~ATTR_BOLD;
2629 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2630 if ((c & 0x8) || vt52_bold)
2631 curr_attr |= ATTR_BOLD;
2634 erase_char = (' ' | ATTR_ASCII |
2635 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2638 termstate = TOPLEVEL;
2639 curr_attr &= ~ATTR_BGMASK;
2640 curr_attr &= ~ATTR_BLINK;
2641 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2643 /* Note: bold background */
2645 curr_attr |= ATTR_BLINK;
2648 erase_char = (' ' | ATTR_ASCII |
2649 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2652 default: break; /* placate gcc warning about enum use */
2654 if (selstate != NO_SELECTION) {
2655 pos cursplus = curs;
2657 check_selection(curs, cursplus);
2664 * Compare two lines to determine whether they are sufficiently
2665 * alike to scroll-optimise one to the other. Return the degree of
2668 static int linecmp(unsigned long *a, unsigned long *b)
2672 for (i = n = 0; i < cols; i++)
2673 n += (*a++ == *b++);
2679 * Given a context, update the window. Out of paranoia, we don't
2680 * allow WM_PAINT responses to do scrolling optimisations.
2682 static void do_paint(Context ctx, int may_optimise)
2684 int i, j, our_curs_y;
2685 unsigned long rv, cursor;
2688 long cursor_background = ERASE_CHAR;
2689 unsigned long ticks;
2692 * Check the visual bell state.
2695 ticks = GetTickCount();
2696 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2700 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2703 * screen array, disptop, scrtop,
2705 * cfg.blinkpc, blink_is_real, tblinker,
2706 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2709 /* Has the cursor position or type changed ? */
2712 if (blinker || !cfg.blink_cur)
2713 cursor = TATTR_ACTCURS;
2717 cursor = TATTR_PASCURS;
2719 cursor |= TATTR_RIGHTCURS;
2722 our_curs_y = curs.y - disptop;
2724 if (dispcurs && (curstype != cursor ||
2726 disptext + our_curs_y * (cols + 1) + curs.x)) {
2727 if (dispcurs > disptext &&
2728 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2729 dispcurs[-1] |= ATTR_INVALID;
2730 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2731 dispcurs[1] |= ATTR_INVALID;
2732 *dispcurs |= ATTR_INVALID;
2737 /* The normal screen data */
2738 for (i = 0; i < rows; i++) {
2739 unsigned long *ldata;
2741 int idx, dirty_line, dirty_run, selected;
2742 unsigned long attr = 0;
2743 int updated_line = 0;
2746 int last_run_dirty = 0;
2748 scrpos.y = i + disptop;
2749 ldata = lineptr(scrpos.y);
2750 lattr = (ldata[cols] & LATTR_MODE);
2752 idx = i * (cols + 1);
2753 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2754 disptext[idx + cols] = ldata[cols];
2756 for (j = 0; j < cols; j++, idx++) {
2757 unsigned long tattr, tchar;
2758 unsigned long *d = ldata + j;
2762 tchar = (*d & (CHAR_MASK | CSET_MASK));
2763 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2764 switch (tchar & CSET_MASK) {
2766 tchar = unitab_line[tchar & 0xFF];
2769 tchar = unitab_xterm[tchar & 0xFF];
2772 tchar = unitab_scoacs[tchar&0xFF];
2775 tattr |= (tchar & CSET_MASK);
2777 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2780 /* Video reversing things */
2781 if (seltype == LEXICOGRAPHIC)
2782 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2784 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2786 ^ (selected ? ATTR_REVERSE : 0));
2788 /* 'Real' blinking ? */
2789 if (blink_is_real && (tattr & ATTR_BLINK)) {
2790 if (has_focus && tblinker) {
2792 tattr &= ~CSET_MASK;
2795 tattr &= ~ATTR_BLINK;
2799 * Check the font we'll _probably_ be using to see if
2800 * the character is wide when we don't want it to be.
2802 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2803 if ((tattr & ATTR_WIDE) == 0 &&
2804 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2805 tattr |= ATTR_NARROW;
2806 } else if (disptext[idx]&ATTR_NARROW)
2807 tattr |= ATTR_NARROW;
2809 /* Cursor here ? Save the 'background' */
2810 if (i == our_curs_y && j == curs.x) {
2811 cursor_background = tattr | tchar;
2812 dispcurs = disptext + idx;
2815 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2818 break_run = (tattr != attr || j - start >= sizeof(ch));
2820 /* Special hack for VT100 Linedraw glyphs */
2821 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2822 && tchar <= 0xBD) break_run = TRUE;
2824 if (!dbcs_screenfont && !dirty_line) {
2825 if ((tchar | tattr) == disptext[idx])
2827 else if (!dirty_run && ccount == 1)
2832 if ((dirty_run || last_run_dirty) && ccount > 0) {
2833 do_text(ctx, start, i, ch, ccount, attr, lattr);
2839 if (dbcs_screenfont)
2840 last_run_dirty = dirty_run;
2841 dirty_run = dirty_line;
2844 if ((tchar | tattr) != disptext[idx])
2846 ch[ccount++] = (char) tchar;
2847 disptext[idx] = tchar | tattr;
2849 /* If it's a wide char step along to the next one. */
2850 if (tattr & ATTR_WIDE) {
2854 /* Cursor is here ? Ouch! */
2855 if (i == our_curs_y && j == curs.x) {
2856 cursor_background = *d;
2857 dispcurs = disptext + idx;
2859 if (disptext[idx] != *d)
2865 if (dirty_run && ccount > 0) {
2866 do_text(ctx, start, i, ch, ccount, attr, lattr);
2870 /* Cursor on this line ? (and changed) */
2871 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2872 ch[0] = (char) (cursor_background & CHAR_MASK);
2873 attr = (cursor_background & ATTR_MASK) | cursor;
2874 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2881 * Flick the switch that says if blinking things should be shown or hidden.
2884 void term_blink(int flg)
2886 static long last_blink = 0;
2887 static long last_tblink = 0;
2888 long now, blink_diff;
2890 now = GetTickCount();
2891 blink_diff = now - last_tblink;
2893 /* Make sure the text blinks no more than 2Hz */
2894 if (blink_diff < 0 || blink_diff > 450) {
2896 tblinker = !tblinker;
2905 blink_diff = now - last_blink;
2907 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2908 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2916 * Invalidate the whole screen so it will be repainted in full.
2918 void term_invalidate(void)
2922 for (i = 0; i < rows * (cols + 1); i++)
2923 disptext[i] = ATTR_INVALID;
2927 * Paint the window in response to a WM_PAINT message.
2929 void term_paint(Context ctx, int left, int top, int right, int bottom)
2932 if (left < 0) left = 0;
2933 if (top < 0) top = 0;
2934 if (right >= cols) right = cols-1;
2935 if (bottom >= rows) bottom = rows-1;
2937 for (i = top; i <= bottom && i < rows; i++) {
2938 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2939 for (j = left; j <= right && j < cols; j++)
2940 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2942 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2943 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2946 /* This should happen soon enough, also for some reason it sometimes
2947 * fails to actually do anything when re-sizing ... painting the wrong
2951 do_paint (ctx, FALSE);
2955 * Attempt to scroll the scrollback. The second parameter gives the
2956 * position we want to scroll to; the first is +1 to denote that
2957 * this position is relative to the beginning of the scrollback, -1
2958 * to denote it is relative to the end, and 0 to denote that it is
2959 * relative to the current position.
2961 void term_scroll(int rel, int where)
2963 int sbtop = -count234(scrollback);
2965 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2966 if (disptop < sbtop)
2974 static void clipme(pos top, pos bottom, int rect)
2977 wchar_t *wbptr; /* where next char goes within workbuf */
2979 int wblen = 0; /* workbuf len */
2980 int buflen; /* amount of memory allocated to workbuf */
2982 buflen = 5120; /* Default size */
2983 workbuf = smalloc(buflen * sizeof(wchar_t));
2984 wbptr = workbuf; /* start filling here */
2985 old_top_x = top.x; /* needed for rect==1 */
2987 while (poslt(top, bottom)) {
2989 unsigned long *ldata = lineptr(top.y);
2993 * nlpos will point at the maximum position on this line we
2994 * should copy up to. So we start it at the end of the
3001 * ... move it backwards if there's unused space at the end
3002 * of the line (and also set `nl' if this is the case,
3003 * because in normal selection mode this means we need a
3004 * newline at the end)...
3006 if (!(ldata[cols] & LATTR_WRAPPED)) {
3007 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3008 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3009 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3010 && poslt(top, nlpos))
3012 if (poslt(nlpos, bottom))
3017 * ... and then clip it to the terminal x coordinate if
3018 * we're doing rectangular selection. (In this case we
3019 * still did the above, so that copying e.g. the right-hand
3020 * column from a table doesn't fill with spaces on the
3024 if (nlpos.x > bottom.x)
3026 nl = (top.y < bottom.y);
3029 while (poslt(top, bottom) && poslt(top, nlpos)) {
3032 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3034 wchar_t cbuf[16], *p;
3035 int uc = (ldata[top.x] & 0xFFFF);
3038 if (uc == UCSWIDE) {
3043 switch (uc & CSET_MASK) {
3046 uc = unitab_xterm[uc & 0xFF];
3050 uc = unitab_line[uc & 0xFF];
3053 uc = unitab_scoacs[uc&0xFF];
3056 switch (uc & CSET_MASK) {
3058 uc = unitab_font[uc & 0xFF];
3061 uc = unitab_oemcp[uc & 0xFF];
3065 set = (uc & CSET_MASK);
3066 c = (uc & CHAR_MASK);
3070 if (DIRECT_FONT(uc)) {
3071 if (c >= ' ' && c != 0x7F) {
3072 unsigned char buf[4];
3075 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
3077 buf[1] = (unsigned char) ldata[top.x + 1];
3078 rv = MultiByteToWideChar(font_codepage,
3079 0, buf, 2, wbuf, 4);
3083 rv = MultiByteToWideChar(font_codepage,
3084 0, buf, 1, wbuf, 4);
3088 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3095 for (p = cbuf; *p; p++) {
3096 /* Enough overhead for trailing NL and nul */
3097 if (wblen >= buflen - 16) {
3100 sizeof(wchar_t) * (buflen += 100));
3101 wbptr = workbuf + wblen;
3110 for (i = 0; i < sel_nl_sz; i++) {
3112 *wbptr++ = sel_nl[i];
3116 top.x = rect ? old_top_x : 0;
3120 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3121 if (buflen > 0) /* indicates we allocated this buffer */
3125 void term_copyall(void)
3128 top.y = -count234(scrollback);
3130 clipme(top, curs, 0);
3134 * The wordness array is mainly for deciding the disposition of the US-ASCII
3137 static int wordtype(int uc)
3140 int start, end, ctype;
3141 } *wptr, ucs_words[] = {
3147 0x037e, 0x037e, 1}, /* Greek question mark */
3149 0x0387, 0x0387, 1}, /* Greek ano teleia */
3151 0x055a, 0x055f, 1}, /* Armenian punctuation */
3153 0x0589, 0x0589, 1}, /* Armenian full stop */
3155 0x0700, 0x070d, 1}, /* Syriac punctuation */
3157 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3159 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3161 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3163 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3165 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3167 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3169 0x2000, 0x200a, 0}, /* Various spaces */
3171 0x2070, 0x207f, 2}, /* superscript */
3173 0x2080, 0x208f, 2}, /* subscript */
3175 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3177 0x3000, 0x3000, 0}, /* ideographic space */
3179 0x3001, 0x3020, 1}, /* ideographic punctuation */
3181 0x303f, 0x309f, 3}, /* Hiragana */
3183 0x30a0, 0x30ff, 3}, /* Katakana */
3185 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3187 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3189 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3191 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3193 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3195 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3197 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3199 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3201 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3206 uc &= (CSET_MASK | CHAR_MASK);
3208 switch (uc & CSET_MASK) {
3210 uc = unitab_xterm[uc & 0xFF];
3213 uc = unitab_line[uc & 0xFF];
3216 uc = unitab_scoacs[uc&0xFF];
3219 switch (uc & CSET_MASK) {
3221 uc = unitab_font[uc & 0xFF];
3224 uc = unitab_oemcp[uc & 0xFF];
3228 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3229 * fail as there's such a thing as a double width space. :-(
3231 if (dbcs_screenfont && font_codepage == line_codepage)
3235 return wordness[uc];
3237 for (wptr = ucs_words; wptr->start; wptr++) {
3238 if (uc >= wptr->start && uc <= wptr->end)
3246 * Spread the selection outwards according to the selection mode.
3248 static pos sel_spread_half(pos p, int dir)
3250 unsigned long *ldata;
3252 int topy = -count234(scrollback);
3254 ldata = lineptr(p.y);
3259 * In this mode, every character is a separate unit, except
3260 * for runs of spaces at the end of a non-wrapping line.
3262 if (!(ldata[cols] & LATTR_WRAPPED)) {
3263 unsigned long *q = ldata + cols;
3264 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3266 if (q == ldata + cols)
3268 if (p.x >= q - ldata)
3269 p.x = (dir == -1 ? q - ldata : cols - 1);
3274 * In this mode, the units are maximal runs of characters
3275 * whose `wordness' has the same value.
3277 wvalue = wordtype(ldata[p.x]);
3281 if (wordtype(ldata[p.x + 1]) == wvalue)
3286 if (ldata[cols] & LATTR_WRAPPED) {
3287 unsigned long *ldata2;
3288 ldata2 = lineptr(p.y+1);
3289 if (wordtype(ldata2[0]) == wvalue) {
3302 if (wordtype(ldata[p.x - 1]) == wvalue)
3307 unsigned long *ldata2;
3310 ldata2 = lineptr(p.y-1);
3311 if ((ldata2[cols] & LATTR_WRAPPED) &&
3312 wordtype(ldata2[cols-1]) == wvalue) {
3324 * In this mode, every line is a unit.
3326 p.x = (dir == -1 ? 0 : cols - 1);
3332 static void sel_spread(void)
3334 if (seltype == LEXICOGRAPHIC) {
3335 selstart = sel_spread_half(selstart, -1);
3337 selend = sel_spread_half(selend, +1);
3342 void term_do_paste(void)
3347 get_clip(&data, &len);
3352 sfree(paste_buffer);
3353 paste_pos = paste_hold = paste_len = 0;
3354 paste_buffer = smalloc(len * sizeof(wchar_t));
3357 while (p < data + len) {
3358 while (p < data + len &&
3359 !(p <= data + len - sel_nl_sz &&
3360 !memcmp(p, sel_nl, sizeof(sel_nl))))
3365 for (i = 0; i < p - q; i++) {
3366 paste_buffer[paste_len++] = q[i];
3370 if (p <= data + len - sel_nl_sz &&
3371 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3372 paste_buffer[paste_len++] = '\r';
3378 /* Assume a small paste will be OK in one go. */
3379 if (paste_len < 256) {
3380 luni_send(paste_buffer, paste_len, 0);
3382 sfree(paste_buffer);
3384 paste_pos = paste_hold = paste_len = 0;
3387 get_clip(NULL, NULL);
3390 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3391 int shift, int ctrl, int alt)
3394 unsigned long *ldata;
3395 int raw_mouse = (xterm_mouse &&
3396 !cfg.no_mouse_rep &&
3397 !(cfg.mouse_override && shift));
3398 int default_seltype;
3402 if (a == MA_DRAG && !raw_mouse)
3407 if (a == MA_DRAG && !raw_mouse)
3420 selpoint.y = y + disptop;
3422 ldata = lineptr(selpoint.y);
3423 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3427 int encstate = 0, r, c;
3429 static int is_down = 0;
3433 encstate = 0x20; /* left button down */
3444 case MBT_WHEEL_DOWN:
3447 default: break; /* placate gcc warning about enum use */
3451 if (xterm_mouse == 1)
3464 default: break; /* placate gcc warning about enum use */
3473 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3474 ldisc_send(abuf, 6, 0);
3478 b = translate_button(b);
3481 * Set the selection type (rectangular or normal) at the start
3482 * of a selection attempt, from the state of Alt.
3484 if (!alt ^ !cfg.rect_select)
3485 default_seltype = RECTANGULAR;
3487 default_seltype = LEXICOGRAPHIC;
3489 if (selstate == NO_SELECTION) {
3490 seltype = default_seltype;
3493 if (b == MBT_SELECT && a == MA_CLICK) {
3495 selstate = ABOUT_TO;
3496 seltype = default_seltype;
3497 selanchor = selpoint;
3499 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3501 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3502 selstate = DRAGGING;
3503 selstart = selanchor = selpoint;
3507 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3508 (b == MBT_EXTEND && a != MA_RELEASE)) {
3509 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3511 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3512 if (seltype == LEXICOGRAPHIC) {
3514 * For normal selection, we extend by moving
3515 * whichever end of the current selection is closer
3518 if (posdiff(selpoint, selstart) <
3519 posdiff(selend, selstart) / 2) {
3523 selanchor = selstart;
3527 * For rectangular selection, we have a choice of
3528 * _four_ places to put selanchor and selpoint: the
3529 * four corners of the selection.
3531 if (2*selpoint.x < selstart.x + selend.x)
3532 selanchor.x = selend.x-1;
3534 selanchor.x = selstart.x;
3536 if (2*selpoint.y < selstart.y + selend.y)
3537 selanchor.y = selend.y;
3539 selanchor.y = selstart.y;
3541 selstate = DRAGGING;
3543 if (selstate != ABOUT_TO && selstate != DRAGGING)
3544 selanchor = selpoint;
3545 selstate = DRAGGING;
3546 if (seltype == LEXICOGRAPHIC) {
3548 * For normal selection, we set (selstart,selend) to
3549 * (selpoint,selanchor) in some order.
3551 if (poslt(selpoint, selanchor)) {
3552 selstart = selpoint;
3556 selstart = selanchor;
3562 * For rectangular selection, we may need to
3563 * interchange x and y coordinates (if the user has
3564 * dragged in the -x and +y directions, or vice versa).
3566 selstart.x = min(selanchor.x, selpoint.x);
3567 selend.x = 1+max(selanchor.x, selpoint.x);
3568 selstart.y = min(selanchor.y, selpoint.y);
3569 selend.y = max(selanchor.y, selpoint.y);
3572 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3573 if (selstate == DRAGGING) {
3575 * We've completed a selection. We now transfer the
3576 * data to the clipboard.
3578 clipme(selstart, selend, (seltype == RECTANGULAR));
3579 selstate = SELECTED;
3581 selstate = NO_SELECTION;
3582 } else if (b == MBT_PASTE
3583 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3594 sfree(paste_buffer);
3601 static long last_paste = 0;
3602 long now, paste_diff;
3607 /* Don't wait forever to paste */
3609 now = GetTickCount();
3610 paste_diff = now - last_paste;
3611 if (paste_diff >= 0 && paste_diff < 450)
3616 while (paste_pos < paste_len) {
3618 while (n + paste_pos < paste_len) {
3619 if (paste_buffer[paste_pos + n++] == '\r')
3622 luni_send(paste_buffer + paste_pos, n, 0);
3625 if (paste_pos < paste_len) {
3630 sfree(paste_buffer);
3635 static void deselect(void)
3637 selstate = NO_SELECTION;
3638 selstart.x = selstart.y = selend.x = selend.y = 0;
3641 void term_deselect(void)
3647 int term_ldisc(int option)
3649 if (option == LD_ECHO)
3650 return term_echoing;
3651 if (option == LD_EDIT)
3652 return term_editing;
3657 * from_backend(), to get data from the backend for the terminal.
3659 int from_backend(int is_stderr, char *data, int len)
3663 bufchain_add(&inbuf, data, len);
3666 * term_out() always completely empties inbuf. Therefore,
3667 * there's no reason at all to return anything other than zero
3668 * from this function, because there _can't_ be a question of
3669 * the remote side needing to wait until term_out() has cleared
3672 * This is a slightly suboptimal way to deal with SSH2 - in
3673 * principle, the window mechanism would allow us to continue
3674 * to accept data on forwarded ports and X connections even
3675 * while the terminal processing was going slowly - but we
3676 * can't do the 100% right thing without moving the terminal
3677 * processing into a separate thread, and that might hurt
3678 * portability. So we manage stdout buffering the old SSH1 way:
3679 * if the terminal processing goes slowly, the whole SSH
3680 * connection stops accepting data until it's ready.
3682 * In practice, I can't imagine this causing serious trouble.