12 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
13 #define CL_VT100 0x0002 /* VT100 */
14 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
15 #define CL_VT102 0x0008 /* VT102 */
16 #define CL_VT220 0x0010 /* VT220 */
17 #define CL_VT320 0x0020 /* VT320 */
18 #define CL_VT420 0x0040 /* VT420 */
19 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
20 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
21 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
22 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
23 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
25 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
26 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
27 #define TM_VT102 (TM_VT100AVO|CL_VT102)
28 #define TM_VT220 (TM_VT102|CL_VT220)
29 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
30 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
32 #define TM_PUTTY (0xFFFF)
34 #define compatibility(x) \
35 if ( ((CL_##x)&compatibility_level) == 0 ) { \
39 #define compatibility2(x,y) \
40 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
45 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
47 static int compatibility_level = TM_PUTTY;
49 static tree234 *scrollback; /* lines scrolled off top of screen */
50 static tree234 *screen; /* lines on primary screen */
51 static tree234 *alt_screen; /* lines on alternate screen */
52 static int disptop; /* distance scrolled back (0 or -ve) */
54 static unsigned long *cpos; /* cursor position (convenience) */
56 static unsigned long *disptext; /* buffer of text on real screen */
57 static unsigned long *dispcurs; /* location of cursor on real screen */
58 static unsigned long curstype; /* type of cursor on real screen */
60 #define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
63 struct beeptime *next;
66 static struct beeptime *beephead, *beeptail;
71 #define TSIZE (sizeof(unsigned long))
72 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
74 static unsigned long curr_attr, save_attr;
75 static unsigned long erase_char = ERASE_CHAR;
80 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
81 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
82 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
83 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
84 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
85 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
87 /* Product-order comparisons for rectangular block selection. */
88 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
89 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
91 static bufchain inbuf; /* terminal input buffer */
92 static pos curs; /* cursor */
93 static pos savecurs; /* saved cursor position */
94 static int marg_t, marg_b; /* scroll margins */
95 static int dec_om; /* DEC origin mode flag */
96 static int wrap, wrapnext; /* wrap flags */
97 static int insert; /* insert-mode flag */
98 static int cset; /* 0 or 1: which char set */
99 static int save_cset, save_csattr; /* saved with cursor position */
100 static int save_utf, save_wnext; /* saved with cursor position */
101 static int rvideo; /* global reverse video flag */
102 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
103 static int cursor_on; /* cursor enabled flag */
104 static int reset_132; /* Flag ESC c resets to 80 cols */
105 static int use_bce; /* Use Background coloured erase */
106 static int blinker; /* When blinking is the cursor on ? */
107 static int tblinker; /* When the blinking text is on */
108 static int blink_is_real; /* Actually blink blinking text */
109 static int term_echoing; /* Does terminal want local echo? */
110 static int term_editing; /* Does terminal want local edit? */
111 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
112 static int vt52_bold; /* Force bold on non-bold colours */
113 static int utf_state; /* Is there a pending UTF-8 character */
114 static int utf_char; /* and what is it so far. */
115 static int utf_size; /* The size of the UTF character. */
116 static int printing, only_printing; /* Are we doing ANSI printing? */
117 static int print_state; /* state of print-end-sequence scan */
118 static bufchain printer_buf; /* buffered data for printer */
119 static printer_job *print_job;
121 static int xterm_mouse; /* send mouse messages to app */
123 static unsigned long cset_attr[2];
126 * Saved settings on the alternate screen.
128 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
129 static int alt_t, alt_b;
130 static int alt_which;
132 #define ARGS_MAX 32 /* max # of esc sequence arguments */
133 #define ARG_DEFAULT 0 /* if an arg isn't specified */
134 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
135 static int esc_args[ARGS_MAX];
136 static int esc_nargs;
137 static int esc_query;
138 #define ANSI(x,y) ((x)+((y)<<8))
139 #define ANSI_QUE(x) ANSI(x,TRUE)
141 #define OSC_STR_MAX 2048
142 static int osc_strlen;
143 static char osc_string[OSC_STR_MAX + 1];
146 static char id_string[1024] = "\033[?6c";
148 static unsigned char *tabs;
160 OSC_STRING, OSC_MAYBE_ST,
169 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
172 LEXICOGRAPHIC, RECTANGULAR
175 SM_CHAR, SM_WORD, SM_LINE
177 static pos selstart, selend, selanchor;
179 static short wordness[256] = {
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
182 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
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, 2, /* 45 */
186 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
187 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
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, /* 89 */
190 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
191 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
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, /* CD */
194 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
195 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
198 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
199 static wchar_t sel_nl[] = SEL_NL;
200 static wchar_t *paste_buffer = 0;
201 static int paste_len, paste_pos, paste_hold;
204 * Internal prototypes.
206 static void do_paint(Context, int);
207 static void erase_lots(int, int, int);
208 static void swap_screen(int);
209 static void update_sbar(void);
210 static void deselect(void);
211 static void term_print_finish(void);
214 * Resize a line to make it `cols' columns wide.
216 unsigned long *resizeline(unsigned long *line, int cols)
219 unsigned long lineattrs;
221 if (line[0] != (unsigned long)cols) {
223 * This line is the wrong length, which probably means it
224 * hasn't been accessed since a resize. Resize it now.
227 lineattrs = line[oldlen + 1];
228 line = srealloc(line, TSIZE * (2 + cols));
230 for (i = oldlen; i < cols; i++)
231 line[i + 1] = ERASE_CHAR;
232 line[cols + 1] = lineattrs & LATTR_MODE;
239 * Retrieve a line of the screen or of the scrollback, according to
240 * whether the y coordinate is non-negative or negative
243 unsigned long *lineptr(int y, int lineno)
245 unsigned long *line, *newline;
253 whichtree = scrollback;
254 treeindex = y + count234(scrollback);
256 line = index234(whichtree, treeindex);
258 /* We assume that we don't screw up and retrieve something out of range. */
259 assert(line != NULL);
261 newline = resizeline(line, cols);
262 if (newline != line) {
263 delpos234(whichtree, treeindex);
264 addpos234(whichtree, newline, treeindex);
271 #define lineptr(x) lineptr(x,__LINE__)
273 * Set up power-on settings for the terminal.
275 static void power_on(void)
277 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
280 alt_b = marg_b = rows - 1;
285 for (i = 0; i < cols; i++)
286 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
288 alt_om = dec_om = cfg.dec_om;
289 alt_wnext = wrapnext = alt_ins = insert = FALSE;
290 alt_wrap = wrap = cfg.wrap_mode;
293 alt_sco_acs = sco_acs = 0;
294 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
299 save_attr = curr_attr = ATTR_DEFAULT;
300 term_editing = term_echoing = FALSE;
301 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
302 app_cursor_keys = cfg.app_cursor;
303 app_keypad_keys = cfg.app_keypad;
305 blink_is_real = cfg.blinktext;
306 erase_char = ERASE_CHAR;
311 for (i = 0; i < 256; i++)
312 wordness[i] = cfg.wordness[i];
316 erase_lots(FALSE, TRUE, TRUE);
318 erase_lots(FALSE, TRUE, TRUE);
323 * Force a screen update.
325 void term_update(void)
330 int need_sbar_update = seen_disp_event;
331 if ((seen_key_event && (cfg.scroll_on_key)) ||
332 (seen_disp_event && (cfg.scroll_on_disp))) {
333 disptop = 0; /* return to main screen */
334 seen_disp_event = seen_key_event = 0;
335 need_sbar_update = TRUE;
337 if (need_sbar_update)
340 sys_cursor(curs.x, curs.y - disptop);
346 * Same as power_on(), but an external function.
348 void term_pwron(void)
358 * When the user reconfigures us, we need to check the forbidden-
359 * alternate-screen config option, disable raw mouse mode if the
360 * user has disabled mouse reporting, and abandon a print job if
361 * the user has disabled printing.
363 void term_reconfig(void)
365 if (cfg.no_alt_screen)
367 if (cfg.no_mouse_rep) {
369 set_raw_mouse_mode(0);
371 if (cfg.no_remote_charset) {
372 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
373 sco_acs = alt_sco_acs = 0;
382 * Clear the scrollback.
384 void term_clrsb(void)
388 while ((line = delpos234(scrollback, 0)) != NULL) {
395 * Initialise the terminal.
399 screen = alt_screen = scrollback = NULL;
401 disptext = dispcurs = NULL;
406 beephead = beeptail = NULL;
409 beep_overloaded = FALSE;
413 * Set up the terminal for a given size.
415 void term_size(int newrows, int newcols, int newsavelines)
418 unsigned long *newdisp, *line;
421 int save_alt_which = alt_which;
423 if (newrows == rows && newcols == cols && newsavelines == savelines)
424 return; /* nothing to do */
430 alt_b = marg_b = newrows - 1;
433 scrollback = newtree234(NULL);
434 screen = newtree234(NULL);
439 * Resize the screen and scrollback. We only need to shift
440 * lines around within our data structures, because lineptr()
441 * will take care of resizing each individual line if
444 * - If the new screen and the old screen differ in length, we
445 * must shunt some lines in from the scrollback or out to
448 * - If doing that fails to provide us with enough material to
449 * fill the new screen (i.e. the number of rows needed in
450 * the new screen exceeds the total number in the previous
451 * screen+scrollback), we must invent some blank lines to
454 * - Then, if the new scrollback length is less than the
455 * amount of scrollback we actually have, we must throw some
458 sblen = count234(scrollback);
459 /* Do this loop to expand the screen if newrows > rows */
460 for (i = rows; i < newrows; i++) {
462 line = delpos234(scrollback, --sblen);
464 line = smalloc(TSIZE * (newcols + 2));
466 for (j = 0; j <= newcols; j++)
467 line[j + 1] = ERASE_CHAR;
469 addpos234(screen, line, 0);
471 /* Do this loop to shrink the screen if newrows < rows */
472 for (i = newrows; i < rows; i++) {
473 line = delpos234(screen, 0);
474 addpos234(scrollback, line, sblen++);
476 assert(count234(screen) == newrows);
477 while (sblen > newsavelines) {
478 line = delpos234(scrollback, 0);
482 assert(count234(scrollback) <= newsavelines);
485 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
486 for (i = 0; i < newrows * (newcols + 1); i++)
487 newdisp[i] = ATTR_INVALID;
492 newalt = newtree234(NULL);
493 for (i = 0; i < newrows; i++) {
494 line = smalloc(TSIZE * (newcols + 2));
496 for (j = 0; j <= newcols; j++)
497 line[j + 1] = erase_char;
498 addpos234(newalt, line, i);
501 while (NULL != (line = delpos234(alt_screen, 0)))
503 freetree234(alt_screen);
507 tabs = srealloc(tabs, newcols * sizeof(*tabs));
510 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
511 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
515 curs.y += newrows - rows;
518 if (curs.y >= newrows)
519 curs.y = newrows - 1;
520 if (curs.x >= newcols)
521 curs.x = newcols - 1;
523 wrapnext = alt_wnext = FALSE;
527 savelines = newsavelines;
530 swap_screen(save_alt_which);
540 static void swap_screen(int which)
545 if (which == alt_which)
572 wrapnext = alt_wnext;
584 sco_acs = alt_sco_acs;
591 * Update the scroll bar.
593 static void update_sbar(void)
597 nscroll = count234(scrollback);
599 set_sbar(nscroll + rows, nscroll + disptop, rows);
603 * Check whether the region bounded by the two pointers intersects
604 * the scroll region, and de-select the on-screen selection if so.
606 static void check_selection(pos from, pos to)
608 if (poslt(from, selend) && poslt(selstart, to))
613 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
614 * for backward.) `sb' is TRUE if the scrolling is permitted to
615 * affect the scrollback buffer.
617 * NB this function invalidates all pointers into lines of the
618 * screen data structures. In particular, you MUST call fix_cpos
619 * after calling scroll() and before doing anything else that
620 * uses the cpos shortcut pointer.
622 static void scroll(int topline, int botline, int lines, int sb)
624 unsigned long *line, *line2;
627 if (topline != 0 || alt_which != 0)
632 line = delpos234(screen, botline);
633 line = resizeline(line, cols);
634 for (i = 0; i < cols; i++)
635 line[i + 1] = erase_char;
637 addpos234(screen, line, topline);
639 if (selstart.y >= topline && selstart.y <= botline) {
641 if (selstart.y > botline) {
642 selstart.y = botline;
646 if (selend.y >= topline && selend.y <= botline) {
648 if (selend.y > botline) {
658 line = delpos234(screen, topline);
659 if (sb && savelines > 0) {
660 int sblen = count234(scrollback);
662 * We must add this line to the scrollback. We'll
663 * remove a line from the top of the scrollback to
664 * replace it, or allocate a new one if the
665 * scrollback isn't full.
667 if (sblen == savelines) {
668 sblen--, line2 = delpos234(scrollback, 0);
670 line2 = smalloc(TSIZE * (cols + 2));
673 addpos234(scrollback, line, sblen);
677 * If the user is currently looking at part of the
678 * scrollback, and they haven't enabled any options
679 * that are going to reset the scrollback as a
680 * result of this movement, then the chances are
681 * they'd like to keep looking at the same line. So
682 * we move their viewpoint at the same rate as the
683 * scroll, at least until their viewpoint hits the
684 * top end of the scrollback buffer, at which point
685 * we don't have the choice any more.
687 * Thanks to Jan Holmen Holsten for the idea and
688 * initial implementation.
690 if (disptop > -savelines && disptop < 0)
693 line = resizeline(line, cols);
694 for (i = 0; i < cols; i++)
695 line[i + 1] = erase_char;
697 addpos234(screen, line, botline);
700 * If the selection endpoints move into the scrollback,
701 * we keep them moving until they hit the top. However,
702 * of course, if the line _hasn't_ moved into the
703 * scrollback then we don't do this, and cut them off
704 * at the top of the scroll region.
706 * This applies to selstart and selend (for an existing
707 * selection), and also selanchor (for one being
708 * selected as we speak).
710 seltop = sb ? -savelines : topline;
712 if (selstart.y >= seltop && selstart.y <= botline) {
714 if (selstart.y < seltop) {
719 if (selend.y >= seltop && selend.y <= botline) {
721 if (selend.y < seltop) {
726 if (selanchor.y >= seltop && selanchor.y <= botline) {
728 if (selanchor.y < seltop) {
729 selanchor.y = seltop;
740 * Move the cursor to a given position, clipping at boundaries. We
741 * may or may not want to clip at the scroll margin: marg_clip is 0
742 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
743 * even _being_ outside the margins.
745 static void move(int x, int y, int marg_clip)
752 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
754 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
768 * Save or restore the cursor and SGR mode.
770 static void save_cursor(int save)
774 save_attr = curr_attr;
777 save_wnext = wrapnext;
778 save_csattr = cset_attr[cset];
779 save_sco_acs = sco_acs;
782 /* Make sure the window hasn't shrunk since the save */
788 curr_attr = save_attr;
791 wrapnext = save_wnext;
793 * wrapnext might reset to False if the x position is no
794 * longer at the rightmost edge.
796 if (wrapnext && curs.x < cols-1)
798 cset_attr[cset] = save_csattr;
799 sco_acs = save_sco_acs;
802 erase_char = (' ' | ATTR_ASCII |
803 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
808 * Erase a large portion of the screen: the whole screen, or the
809 * whole line, or parts thereof.
811 static void erase_lots(int line_only, int from_begin, int to_end)
815 unsigned long *ldata;
837 check_selection(start, end);
839 /* Clear screen also forces a full window redraw, just in case. */
840 if (start.y == 0 && start.x == 0 && end.y == rows)
843 ldata = lineptr(start.y);
844 while (poslt(start, end)) {
845 if (start.x == cols && !erase_lattr)
846 ldata[start.x] &= ~LATTR_WRAPPED;
848 ldata[start.x] = erase_char;
849 if (incpos(start) && start.y < rows)
850 ldata = lineptr(start.y);
855 * Insert or delete characters within the current line. n is +ve if
856 * insertion is desired, and -ve for deletion.
858 static void insch(int n)
860 int dir = (n < 0 ? -1 : +1);
863 unsigned long *ldata;
865 n = (n < 0 ? -n : n);
866 if (n > cols - curs.x)
868 m = cols - curs.x - n;
870 cursplus.x = curs.x + n;
871 check_selection(curs, cursplus);
872 ldata = lineptr(curs.y);
874 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
876 ldata[curs.x + m++] = erase_char;
878 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
880 ldata[curs.x + n] = erase_char;
885 * Toggle terminal mode `mode' to state `state'. (`query' indicates
886 * whether the mode is a DEC private one or a normal one.)
888 static void toggle_mode(int mode, int query, int state)
894 case 1: /* application cursor keys */
895 app_cursor_keys = state;
897 case 2: /* VT52 mode */
900 blink_is_real = FALSE;
903 blink_is_real = cfg.blinktext;
906 case 3: /* 80/132 columns */
908 if (!cfg.no_remote_resize)
909 request_resize(state ? 132 : 80, rows);
912 case 5: /* reverse video */
914 * Toggle reverse video. If we receive an OFF within the
915 * visual bell timeout period after an ON, we trigger an
916 * effective visual bell, so that ESC[?5hESC[?5l will
917 * always be an actually _visible_ visual bell.
919 ticks = GETTICKCOUNT();
920 /* turn off a previous vbell to avoid inconsistencies */
921 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
923 if (rvideo && !state && /* we're turning it off... */
924 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
925 /* If there's no vbell timeout already, or this one lasts
926 * longer, replace vbell_timeout with ours. */
928 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
929 vbell_startpoint = rvbell_startpoint;
930 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
931 } else if (!rvideo && state) {
932 /* This is an ON, so we notice the time and save it. */
933 rvbell_startpoint = ticks;
936 seen_disp_event = TRUE;
940 case 6: /* DEC origin mode */
943 case 7: /* auto wrap */
946 case 8: /* auto key repeat */
949 case 10: /* set local edit mode */
950 term_editing = state;
951 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
953 case 25: /* enable/disable cursor */
954 compatibility2(OTHER, VT220);
956 seen_disp_event = TRUE;
958 case 47: /* alternate screen */
959 compatibility(OTHER);
961 swap_screen(cfg.no_alt_screen ? 0 : state);
964 case 1000: /* xterm mouse 1 */
965 xterm_mouse = state ? 1 : 0;
966 set_raw_mouse_mode(state);
968 case 1002: /* xterm mouse 2 */
969 xterm_mouse = state ? 2 : 0;
970 set_raw_mouse_mode(state);
974 case 4: /* set insert mode */
975 compatibility(VT102);
978 case 12: /* set echo mode */
979 term_echoing = !state;
980 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
982 case 20: /* Return sends ... */
983 cr_lf_return = state;
985 case 34: /* Make cursor BIG */
986 compatibility2(OTHER, VT220);
992 * Process an OSC sequence: set window title or icon name.
994 static void do_osc(void)
998 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
1000 osc_string[osc_strlen] = '\0';
1001 switch (esc_args[0]) {
1004 if (!cfg.no_remote_wintitle)
1005 set_icon(osc_string);
1006 if (esc_args[0] == 1)
1008 /* fall through: parameter 0 means set both */
1011 if (!cfg.no_remote_wintitle)
1012 set_title(osc_string);
1019 * ANSI printing routines.
1021 static void term_print_setup(void)
1023 bufchain_clear(&printer_buf);
1024 print_job = printer_start_job(cfg.printer);
1026 static void term_print_flush(void)
1031 while ((size = bufchain_size(&printer_buf)) > 5) {
1032 bufchain_prefix(&printer_buf, &data, &len);
1035 printer_job_data(print_job, data, len);
1036 bufchain_consume(&printer_buf, len);
1039 static void term_print_finish(void)
1046 while ((size = bufchain_size(&printer_buf)) > 0) {
1047 bufchain_prefix(&printer_buf, &data, &len);
1049 if (c == '\033' || c == '\233') {
1050 bufchain_consume(&printer_buf, size);
1053 printer_job_data(print_job, &c, 1);
1054 bufchain_consume(&printer_buf, 1);
1057 printer_finish_job(print_job);
1059 printing = only_printing = FALSE;
1063 * Remove everything currently in `inbuf' and stick it up on the
1064 * in-memory display. There's a big state machine in here to
1065 * process escape sequences...
1070 unsigned char localbuf[256], *chars;
1075 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1079 bufchain_prefix(&inbuf, &ret, &nchars);
1080 if (nchars > sizeof(localbuf))
1081 nchars = sizeof(localbuf);
1082 memcpy(localbuf, ret, nchars);
1083 bufchain_consume(&inbuf, nchars);
1085 assert(chars != NULL);
1091 * Optionally log the session traffic to a file. Useful for
1092 * debugging and possibly also useful for actual logging.
1094 if (cfg.logtype == LGTYP_DEBUG)
1095 logtraffic((unsigned char) c, LGTYP_DEBUG);
1101 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1102 * be able to display 8-bit characters, but I'll let that go 'cause
1107 * If we're printing, add the character to the printer
1111 bufchain_add(&printer_buf, &c, 1);
1114 * If we're in print-only mode, we use a much simpler
1115 * state machine designed only to recognise the ESC[4i
1116 * termination sequence.
1118 if (only_printing) {
1121 else if (c == (unsigned char)'\233')
1123 else if (c == '[' && print_state == 1)
1125 else if (c == '4' && print_state == 2)
1127 else if (c == 'i' && print_state == 3)
1131 if (print_state == 4) {
1132 printing = only_printing = FALSE;
1133 term_print_finish();
1139 /* First see about all those translations. */
1140 if (termstate == TOPLEVEL) {
1142 switch (utf_state) {
1145 /* UTF-8 must be stateless so we ignore iso2022. */
1146 if (unitab_ctrl[c] != 0xFF)
1148 else c = ((unsigned char)c) | ATTR_ASCII;
1150 } else if ((c & 0xe0) == 0xc0) {
1151 utf_size = utf_state = 1;
1152 utf_char = (c & 0x1f);
1153 } else if ((c & 0xf0) == 0xe0) {
1154 utf_size = utf_state = 2;
1155 utf_char = (c & 0x0f);
1156 } else if ((c & 0xf8) == 0xf0) {
1157 utf_size = utf_state = 3;
1158 utf_char = (c & 0x07);
1159 } else if ((c & 0xfc) == 0xf8) {
1160 utf_size = utf_state = 4;
1161 utf_char = (c & 0x03);
1162 } else if ((c & 0xfe) == 0xfc) {
1163 utf_size = utf_state = 5;
1164 utf_char = (c & 0x01);
1175 if ((c & 0xC0) != 0x80) {
1181 utf_char = (utf_char << 6) | (c & 0x3f);
1187 /* Is somebody trying to be evil! */
1189 (c < 0x800 && utf_size >= 2) ||
1190 (c < 0x10000 && utf_size >= 3) ||
1191 (c < 0x200000 && utf_size >= 4) ||
1192 (c < 0x4000000 && utf_size >= 5))
1195 /* Unicode line separator and paragraph separator are CR-LF */
1196 if (c == 0x2028 || c == 0x2029)
1199 /* High controls are probably a Baaad idea too. */
1203 /* The UTF-16 surrogates are not nice either. */
1204 /* The standard give the option of decoding these:
1205 * I don't want to! */
1206 if (c >= 0xD800 && c < 0xE000)
1209 /* ISO 10646 characters now limited to UTF-16 range. */
1213 /* This is currently a TagPhobic application.. */
1214 if (c >= 0xE0000 && c <= 0xE007F)
1217 /* U+FEFF is best seen as a null. */
1220 /* But U+FFFE is an error. */
1221 if (c == 0xFFFE || c == 0xFFFF)
1224 /* Oops this is a 16bit implementation */
1229 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1231 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1233 if (sco_acs == 2) c ^= 0x80;
1236 switch (cset_attr[cset]) {
1238 * Linedraw characters are different from 'ESC ( B'
1239 * only for a small range. For ones outside that
1240 * range, make sure we use the same font as well as
1241 * the same encoding.
1244 if (unitab_ctrl[c] != 0xFF)
1247 c = ((unsigned char) c) | ATTR_LINEDRW;
1251 /* If UK-ASCII, make the '#' a LineDraw Pound */
1253 c = '}' | ATTR_LINEDRW;
1256 /*FALLTHROUGH*/ case ATTR_ASCII:
1257 if (unitab_ctrl[c] != 0xFF)
1260 c = ((unsigned char) c) | ATTR_ASCII;
1263 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1269 /* How about C1 controls ? */
1270 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1271 has_compat(VT220)) {
1272 termstate = SEEN_ESC;
1274 c = '@' + (c & 0x1F);
1277 /* Or the GL control. */
1278 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1279 if (curs.x && !wrapnext)
1283 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1284 *cpos = (' ' | curr_attr | ATTR_ASCII);
1286 /* Or normal C0 controls. */
1287 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1289 case '\005': /* terminal type query */
1290 /* Strictly speaking this is VT100 but a VT100 defaults to
1291 * no response. Other terminals respond at their option.
1293 * Don't put a CR in the default string as this tends to
1294 * upset some weird software.
1296 * An xterm returns "xterm" (5 characters)
1298 compatibility(ANSIMIN);
1300 char abuf[256], *s, *d;
1302 for (s = cfg.answerback, d = abuf; *s; s++) {
1304 if (*s >= 'a' && *s <= 'z')
1305 *d++ = (*s - ('a' - 1));
1306 else if ((*s >= '@' && *s <= '_') ||
1307 *s == '?' || (*s & 0x80))
1312 } else if (*s == '^') {
1317 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1322 struct beeptime *newbeep;
1323 unsigned long ticks;
1325 ticks = GETTICKCOUNT();
1327 if (!beep_overloaded) {
1328 newbeep = smalloc(sizeof(struct beeptime));
1329 newbeep->ticks = ticks;
1330 newbeep->next = NULL;
1334 beeptail->next = newbeep;
1340 * Throw out any beeps that happened more than
1344 beephead->ticks < ticks - cfg.bellovl_t) {
1345 struct beeptime *tmp = beephead;
1346 beephead = tmp->next;
1353 if (cfg.bellovl && beep_overloaded &&
1354 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1356 * If we're currently overloaded and the
1357 * last beep was more than s seconds ago,
1358 * leave overload mode.
1360 beep_overloaded = FALSE;
1361 } else if (cfg.bellovl && !beep_overloaded &&
1362 nbeeps >= cfg.bellovl_n) {
1364 * Now, if we have n or more beeps
1365 * remaining in the queue, go into overload
1368 beep_overloaded = TRUE;
1373 * Perform an actual beep if we're not overloaded.
1375 if (!cfg.bellovl || !beep_overloaded) {
1377 if (cfg.beep == BELL_VISUAL) {
1379 vbell_startpoint = ticks;
1387 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1388 else if (curs.x == 0 && curs.y > 0)
1389 curs.x = cols - 1, curs.y--;
1395 seen_disp_event = TRUE;
1398 compatibility(VT100);
1402 compatibility(VT100);
1407 termstate = VT52_ESC;
1409 compatibility(ANSIMIN);
1410 termstate = SEEN_ESC;
1418 seen_disp_event = TRUE;
1420 logtraffic((unsigned char) c, LGTYP_ASCII);
1423 if (has_compat(SCOANSI)) {
1425 erase_lots(FALSE, FALSE, TRUE);
1428 seen_disp_event = 1;
1432 compatibility(VT100);
1434 if (curs.y == marg_b)
1435 scroll(marg_t, marg_b, 1, TRUE);
1436 else if (curs.y < rows - 1)
1442 seen_disp_event = 1;
1444 logtraffic((unsigned char) c, LGTYP_ASCII);
1448 pos old_curs = curs;
1449 unsigned long *ldata = lineptr(curs.y);
1453 } while (curs.x < cols - 1 && !tabs[curs.x]);
1455 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1456 if (curs.x >= cols / 2)
1457 curs.x = cols / 2 - 1;
1464 check_selection(old_curs, curs);
1466 seen_disp_event = TRUE;
1470 switch (termstate) {
1472 /* Only graphic characters get this far, ctrls are stripped above */
1473 if (wrapnext && wrap) {
1474 cpos[1] |= LATTR_WRAPPED;
1475 if (curs.y == marg_b)
1476 scroll(marg_t, marg_b, 1, TRUE);
1477 else if (curs.y < rows - 1)
1485 if (selstate != NO_SELECTION) {
1486 pos cursplus = curs;
1488 check_selection(curs, cursplus);
1490 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1491 logtraffic((unsigned char) c, LGTYP_ASCII);
1493 extern int wcwidth(wchar_t ucs);
1498 width = wcwidth((wchar_t) c);
1501 *cpos++ = c | curr_attr;
1502 if (++curs.x == cols) {
1503 *cpos |= LATTR_WRAPPED;
1504 if (curs.y == marg_b)
1505 scroll(marg_t, marg_b, 1, TRUE);
1506 else if (curs.y < rows - 1)
1511 *cpos++ = UCSWIDE | curr_attr;
1514 *cpos++ = c | curr_attr;
1521 if (curs.x == cols) {
1525 if (wrap && vt52_mode) {
1526 cpos[1] |= LATTR_WRAPPED;
1527 if (curs.y == marg_b)
1528 scroll(marg_t, marg_b, 1, TRUE);
1529 else if (curs.y < rows - 1)
1536 seen_disp_event = 1;
1541 * This state is virtually identical to SEEN_ESC, with the
1542 * exception that we have an OSC sequence in the pipeline,
1543 * and _if_ we see a backslash, we process it.
1547 termstate = TOPLEVEL;
1550 /* else fall through */
1552 if (c >= ' ' && c <= '/') {
1559 termstate = TOPLEVEL;
1560 switch (ANSI(c, esc_query)) {
1561 case '[': /* enter CSI mode */
1562 termstate = SEEN_CSI;
1564 esc_args[0] = ARG_DEFAULT;
1567 case ']': /* xterm escape sequences */
1568 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1569 compatibility(OTHER);
1570 termstate = SEEN_OSC;
1573 case '7': /* save cursor */
1574 compatibility(VT100);
1577 case '8': /* restore cursor */
1578 compatibility(VT100);
1580 seen_disp_event = TRUE;
1583 compatibility(VT100);
1584 app_keypad_keys = TRUE;
1587 compatibility(VT100);
1588 app_keypad_keys = FALSE;
1590 case 'D': /* exactly equivalent to LF */
1591 compatibility(VT100);
1592 if (curs.y == marg_b)
1593 scroll(marg_t, marg_b, 1, TRUE);
1594 else if (curs.y < rows - 1)
1598 seen_disp_event = TRUE;
1600 case 'E': /* exactly equivalent to CR-LF */
1601 compatibility(VT100);
1603 if (curs.y == marg_b)
1604 scroll(marg_t, marg_b, 1, TRUE);
1605 else if (curs.y < rows - 1)
1609 seen_disp_event = TRUE;
1611 case 'M': /* reverse index - backwards LF */
1612 compatibility(VT100);
1613 if (curs.y == marg_t)
1614 scroll(marg_t, marg_b, -1, TRUE);
1615 else if (curs.y > 0)
1619 seen_disp_event = TRUE;
1621 case 'Z': /* terminal type query */
1622 compatibility(VT100);
1623 ldisc_send(id_string, strlen(id_string), 0);
1625 case 'c': /* restore power-on settings */
1626 compatibility(VT100);
1629 if (!cfg.no_remote_resize)
1630 request_resize(80, rows);
1635 seen_disp_event = TRUE;
1637 case 'H': /* set a tab */
1638 compatibility(VT100);
1639 tabs[curs.x] = TRUE;
1642 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1643 compatibility(VT100);
1645 unsigned long *ldata;
1649 for (i = 0; i < rows; i++) {
1651 for (j = 0; j < cols; j++)
1652 ldata[j] = ATTR_DEFAULT | 'E';
1656 seen_disp_event = TRUE;
1657 scrtop.x = scrtop.y = 0;
1660 check_selection(scrtop, scrbot);
1664 case ANSI('3', '#'):
1665 case ANSI('4', '#'):
1666 case ANSI('5', '#'):
1667 case ANSI('6', '#'):
1668 compatibility(VT100);
1670 unsigned long nlattr;
1671 unsigned long *ldata;
1672 switch (ANSI(c, esc_query)) {
1673 case ANSI('3', '#'):
1676 case ANSI('4', '#'):
1679 case ANSI('5', '#'):
1680 nlattr = LATTR_NORM;
1682 default: /* spiritually case ANSI('6', '#'): */
1683 nlattr = LATTR_WIDE;
1686 ldata = lineptr(curs.y);
1687 ldata[cols] &= ~LATTR_MODE;
1688 ldata[cols] |= nlattr;
1692 case ANSI('A', '('):
1693 compatibility(VT100);
1694 if (!cfg.no_remote_charset)
1695 cset_attr[0] = ATTR_GBCHR;
1697 case ANSI('B', '('):
1698 compatibility(VT100);
1699 if (!cfg.no_remote_charset)
1700 cset_attr[0] = ATTR_ASCII;
1702 case ANSI('0', '('):
1703 compatibility(VT100);
1704 if (!cfg.no_remote_charset)
1705 cset_attr[0] = ATTR_LINEDRW;
1707 case ANSI('U', '('):
1708 compatibility(OTHER);
1709 if (!cfg.no_remote_charset)
1710 cset_attr[0] = ATTR_SCOACS;
1713 case ANSI('A', ')'):
1714 compatibility(VT100);
1715 if (!cfg.no_remote_charset)
1716 cset_attr[1] = ATTR_GBCHR;
1718 case ANSI('B', ')'):
1719 compatibility(VT100);
1720 if (!cfg.no_remote_charset)
1721 cset_attr[1] = ATTR_ASCII;
1723 case ANSI('0', ')'):
1724 compatibility(VT100);
1725 if (!cfg.no_remote_charset)
1726 cset_attr[1] = ATTR_LINEDRW;
1728 case ANSI('U', ')'):
1729 compatibility(OTHER);
1730 if (!cfg.no_remote_charset)
1731 cset_attr[1] = ATTR_SCOACS;
1734 case ANSI('8', '%'): /* Old Linux code */
1735 case ANSI('G', '%'):
1736 compatibility(OTHER);
1737 if (!cfg.no_remote_charset)
1740 case ANSI('@', '%'):
1741 compatibility(OTHER);
1742 if (!cfg.no_remote_charset)
1748 termstate = TOPLEVEL; /* default */
1750 if (esc_nargs <= ARGS_MAX) {
1751 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1752 esc_args[esc_nargs - 1] = 0;
1753 esc_args[esc_nargs - 1] =
1754 10 * esc_args[esc_nargs - 1] + c - '0';
1756 termstate = SEEN_CSI;
1757 } else if (c == ';') {
1758 if (++esc_nargs <= ARGS_MAX)
1759 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1760 termstate = SEEN_CSI;
1761 } else if (c < '@') {
1768 termstate = SEEN_CSI;
1770 switch (ANSI(c, esc_query)) {
1771 case 'A': /* move up N lines */
1772 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1773 seen_disp_event = TRUE;
1775 case 'e': /* move down N lines */
1776 compatibility(ANSI);
1779 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1780 seen_disp_event = TRUE;
1782 case ANSI('c', '>'): /* report xterm version */
1783 compatibility(OTHER);
1784 /* this reports xterm version 136 so that VIM can
1785 use the drag messages from the mouse reporting */
1786 ldisc_send("\033[>0;136;0c", 11, 0);
1788 case 'a': /* move right N cols */
1789 compatibility(ANSI);
1792 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1793 seen_disp_event = TRUE;
1795 case 'D': /* move left N cols */
1796 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1797 seen_disp_event = TRUE;
1799 case 'E': /* move down N lines and CR */
1800 compatibility(ANSI);
1801 move(0, curs.y + def(esc_args[0], 1), 1);
1802 seen_disp_event = TRUE;
1804 case 'F': /* move up N lines and CR */
1805 compatibility(ANSI);
1806 move(0, curs.y - def(esc_args[0], 1), 1);
1807 seen_disp_event = TRUE;
1810 case '`': /* set horizontal posn */
1811 compatibility(ANSI);
1812 move(def(esc_args[0], 1) - 1, curs.y, 0);
1813 seen_disp_event = TRUE;
1815 case 'd': /* set vertical posn */
1816 compatibility(ANSI);
1818 (dec_om ? marg_t : 0) + def(esc_args[0],
1821 seen_disp_event = TRUE;
1824 case 'f': /* set horz and vert posns at once */
1826 esc_args[1] = ARG_DEFAULT;
1827 move(def(esc_args[1], 1) - 1,
1828 (dec_om ? marg_t : 0) + def(esc_args[0],
1831 seen_disp_event = TRUE;
1833 case 'J': /* erase screen or parts of it */
1835 unsigned int i = def(esc_args[0], 0) + 1;
1838 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1841 seen_disp_event = TRUE;
1843 case 'K': /* erase line or parts of it */
1845 unsigned int i = def(esc_args[0], 0) + 1;
1848 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1850 seen_disp_event = TRUE;
1852 case 'L': /* insert lines */
1853 compatibility(VT102);
1854 if (curs.y <= marg_b)
1855 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1858 seen_disp_event = TRUE;
1860 case 'M': /* delete lines */
1861 compatibility(VT102);
1862 if (curs.y <= marg_b)
1863 scroll(curs.y, marg_b, def(esc_args[0], 1),
1866 seen_disp_event = TRUE;
1868 case '@': /* insert chars */
1869 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1870 compatibility(VT102);
1871 insch(def(esc_args[0], 1));
1872 seen_disp_event = TRUE;
1874 case 'P': /* delete chars */
1875 compatibility(VT102);
1876 insch(-def(esc_args[0], 1));
1877 seen_disp_event = TRUE;
1879 case 'c': /* terminal type query */
1880 compatibility(VT100);
1881 /* This is the response for a VT102 */
1882 ldisc_send(id_string, strlen(id_string), 0);
1884 case 'n': /* cursor position query */
1885 if (esc_args[0] == 6) {
1887 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1889 ldisc_send(buf, strlen(buf), 0);
1890 } else if (esc_args[0] == 5) {
1891 ldisc_send("\033[0n", 4, 0);
1894 case 'h': /* toggle modes to high */
1896 compatibility(VT100);
1899 for (i = 0; i < esc_nargs; i++)
1900 toggle_mode(esc_args[i], esc_query, TRUE);
1905 compatibility(VT100);
1907 if (esc_nargs != 1) break;
1908 if (esc_args[0] == 5 && *cfg.printer) {
1910 only_printing = !esc_query;
1913 } else if (esc_args[0] == 4 && printing) {
1915 only_printing = FALSE;
1916 term_print_finish();
1920 case 'l': /* toggle modes to low */
1922 compatibility(VT100);
1925 for (i = 0; i < esc_nargs; i++)
1926 toggle_mode(esc_args[i], esc_query, FALSE);
1929 case 'g': /* clear tabs */
1930 compatibility(VT100);
1931 if (esc_nargs == 1) {
1932 if (esc_args[0] == 0) {
1933 tabs[curs.x] = FALSE;
1934 } else if (esc_args[0] == 3) {
1936 for (i = 0; i < cols; i++)
1941 case 'r': /* set scroll margins */
1942 compatibility(VT100);
1943 if (esc_nargs <= 2) {
1945 top = def(esc_args[0], 1) - 1;
1946 bot = (esc_nargs <= 1
1948 0 ? rows : def(esc_args[1], rows)) - 1;
1951 /* VTTEST Bug 9 - if region is less than 2 lines
1952 * don't change region.
1954 if (bot - top > 0) {
1959 * I used to think the cursor should be
1960 * placed at the top of the newly marginned
1961 * area. Apparently not: VMS TPU falls over
1964 * Well actually it should for Origin mode - RDB
1966 curs.y = (dec_om ? marg_t : 0);
1968 seen_disp_event = TRUE;
1972 case 'm': /* set graphics rendition */
1975 * A VT100 without the AVO only had one attribute, either
1976 * underline or reverse video depending on the cursor type,
1977 * this was selected by CSI 7m.
1980 * This is sometimes DIM, eg on the GIGI and Linux
1982 * This is sometimes INVIS various ANSI.
1984 * This like 22 disables BOLD, DIM and INVIS
1986 * The ANSI colours appear on any terminal that has colour
1987 * (obviously) but the interaction between sgr0 and the
1988 * colours varies but is usually related to the background
1989 * colour erase item.
1990 * The interaction between colour attributes and the mono
1991 * ones is also very implementation dependent.
1993 * The 39 and 49 attributes are likely to be unimplemented.
1996 for (i = 0; i < esc_nargs; i++) {
1997 switch (def(esc_args[i], 0)) {
1998 case 0: /* restore defaults */
1999 curr_attr = ATTR_DEFAULT;
2001 case 1: /* enable bold */
2002 compatibility(VT100AVO);
2003 curr_attr |= ATTR_BOLD;
2005 case 21: /* (enable double underline) */
2006 compatibility(OTHER);
2007 case 4: /* enable underline */
2008 compatibility(VT100AVO);
2009 curr_attr |= ATTR_UNDER;
2011 case 5: /* enable blink */
2012 compatibility(VT100AVO);
2013 curr_attr |= ATTR_BLINK;
2015 case 7: /* enable reverse video */
2016 curr_attr |= ATTR_REVERSE;
2018 case 10: /* SCO acs off */
2019 compatibility(SCOANSI);
2020 if (cfg.no_remote_charset) break;
2022 case 11: /* SCO acs on */
2023 compatibility(SCOANSI);
2024 if (cfg.no_remote_charset) break;
2026 case 12: /* SCO acs on flipped */
2027 compatibility(SCOANSI);
2028 if (cfg.no_remote_charset) break;
2030 case 22: /* disable bold */
2031 compatibility2(OTHER, VT220);
2032 curr_attr &= ~ATTR_BOLD;
2034 case 24: /* disable underline */
2035 compatibility2(OTHER, VT220);
2036 curr_attr &= ~ATTR_UNDER;
2038 case 25: /* disable blink */
2039 compatibility2(OTHER, VT220);
2040 curr_attr &= ~ATTR_BLINK;
2042 case 27: /* disable reverse video */
2043 compatibility2(OTHER, VT220);
2044 curr_attr &= ~ATTR_REVERSE;
2055 curr_attr &= ~ATTR_FGMASK;
2057 (esc_args[i] - 30) << ATTR_FGSHIFT;
2059 case 39: /* default-foreground */
2060 curr_attr &= ~ATTR_FGMASK;
2061 curr_attr |= ATTR_DEFFG;
2072 curr_attr &= ~ATTR_BGMASK;
2074 (esc_args[i] - 40) << ATTR_BGSHIFT;
2076 case 49: /* default-background */
2077 curr_attr &= ~ATTR_BGMASK;
2078 curr_attr |= ATTR_DEFBG;
2083 erase_char = (' ' | ATTR_ASCII |
2085 (ATTR_FGMASK | ATTR_BGMASK)));
2088 case 's': /* save cursor */
2091 case 'u': /* restore cursor */
2093 seen_disp_event = TRUE;
2095 case 't': /* set page size - ie window height */
2097 * VT340/VT420 sequence DECSLPP, DEC only allows values
2098 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2099 * illegal values (eg first arg 1..9) for window changing
2103 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2104 compatibility(VT340TEXT);
2105 if (!cfg.no_remote_resize)
2106 request_resize(cols, def(esc_args[0], 24));
2108 } else if (esc_nargs >= 1 &&
2111 compatibility(OTHER);
2113 switch (esc_args[0]) {
2123 if (esc_nargs >= 3) {
2124 if (!cfg.no_remote_resize)
2125 move_window(def(esc_args[1], 0),
2126 def(esc_args[2], 0));
2130 /* We should resize the window to a given
2131 * size in pixels here, but currently our
2132 * resizing code isn't healthy enough to
2136 set_zorder(TRUE); /* move to top */
2139 set_zorder(FALSE); /* move to bottom */
2145 if (esc_nargs >= 3) {
2146 if (!cfg.no_remote_resize)
2147 request_resize(def(esc_args[2], cfg.width),
2148 def(esc_args[1], cfg.height));
2153 set_zoomed(esc_args[1] ? TRUE : FALSE);
2156 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2160 get_window_pos(&x, &y);
2161 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2162 ldisc_send(buf, len, 0);
2165 get_window_pixels(&x, &y);
2166 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2167 ldisc_send(buf, len, 0);
2170 len = sprintf(buf, "\033[8;%d;%dt",
2172 ldisc_send(buf, len, 0);
2176 * Hmmm. Strictly speaking we
2177 * should return `the size of the
2178 * screen in characters', but
2179 * that's not easy: (a) window
2180 * furniture being what it is it's
2181 * hard to compute, and (b) in
2182 * resize-font mode maximising the
2183 * window wouldn't change the
2184 * number of characters. *shrug*. I
2185 * think we'll ignore it for the
2186 * moment and see if anyone
2187 * complains, and then ask them
2188 * what they would like it to do.
2192 p = get_window_title(TRUE);
2194 ldisc_send("\033]L", 3, 0);
2195 ldisc_send(p, len, 0);
2196 ldisc_send("\033\\", 2, 0);
2199 p = get_window_title(FALSE);
2201 ldisc_send("\033]l", 3, 0);
2202 ldisc_send(p, len, 0);
2203 ldisc_send("\033\\", 2, 0);
2209 compatibility(SCOANSI);
2210 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2213 seen_disp_event = TRUE;
2216 compatibility(SCOANSI);
2217 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2220 seen_disp_event = TRUE;
2222 case ANSI('|', '*'):
2223 /* VT420 sequence DECSNLS
2224 * Set number of lines on screen
2225 * VT420 uses VGA like hardware and can support any size in
2226 * reasonable range (24..49 AIUI) with no default specified.
2228 compatibility(VT420);
2229 if (esc_nargs == 1 && esc_args[0] > 0) {
2230 if (!cfg.no_remote_resize)
2231 request_resize(cols, def(esc_args[0], cfg.height));
2235 case ANSI('|', '$'):
2236 /* VT340/VT420 sequence DECSCPP
2237 * Set number of columns per page
2238 * Docs imply range is only 80 or 132, but I'll allow any.
2240 compatibility(VT340TEXT);
2241 if (esc_nargs <= 1) {
2242 if (!cfg.no_remote_resize)
2243 request_resize(def(esc_args[0], cfg.width), rows);
2247 case 'X': /* write N spaces w/o moving cursor */
2248 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2249 compatibility(ANSIMIN);
2251 int n = def(esc_args[0], 1);
2253 unsigned long *p = cpos;
2254 if (n > cols - curs.x)
2258 check_selection(curs, cursplus);
2261 seen_disp_event = TRUE;
2264 case 'x': /* report terminal characteristics */
2265 compatibility(VT100);
2268 int i = def(esc_args[0], 0);
2269 if (i == 0 || i == 1) {
2270 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2272 ldisc_send(buf, 20, 0);
2276 case 'Z': /* BackTab for xterm */
2277 compatibility(OTHER);
2279 int i = def(esc_args[0], 1);
2280 pos old_curs = curs;
2282 for(;i>0 && curs.x>0; i--) {
2285 } while (curs.x >0 && !tabs[curs.x]);
2288 check_selection(old_curs, curs);
2291 case ANSI('L', '='):
2292 compatibility(OTHER);
2293 use_bce = (esc_args[0] <= 0);
2294 erase_char = ERASE_CHAR;
2296 erase_char = (' ' | ATTR_ASCII |
2298 (ATTR_FGMASK | ATTR_BGMASK)));
2300 case ANSI('E', '='):
2301 compatibility(OTHER);
2302 blink_is_real = (esc_args[0] >= 1);
2304 case ANSI('p', '"'):
2305 /* Allow the host to make this emulator a 'perfect' VT102.
2306 * This first appeared in the VT220, but we do need to get
2307 * back to PuTTY mode so I won't check it.
2309 * The arg in 40..42,50 are a PuTTY extension.
2310 * The 2nd arg, 8bit vs 7bit is not checked.
2312 * Setting VT102 mode should also change the Fkeys to
2313 * generate PF* codes as a real VT102 has no Fkeys.
2314 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2317 * Note ESC c will NOT change this!
2320 switch (esc_args[0]) {
2322 compatibility_level &= ~TM_VTXXX;
2323 compatibility_level |= TM_VT102;
2326 compatibility_level &= ~TM_VTXXX;
2327 compatibility_level |= TM_VT220;
2331 if (esc_args[0] > 60 && esc_args[0] < 70)
2332 compatibility_level |= TM_VTXXX;
2336 compatibility_level &= TM_VTXXX;
2339 compatibility_level = TM_PUTTY;
2342 compatibility_level = TM_SCOANSI;
2346 compatibility_level = TM_PUTTY;
2352 /* Change the response to CSI c */
2353 if (esc_args[0] == 50) {
2356 strcpy(id_string, "\033[?");
2357 for (i = 1; i < esc_nargs; i++) {
2359 strcat(id_string, ";");
2360 sprintf(lbuf, "%d", esc_args[i]);
2361 strcat(id_string, lbuf);
2363 strcat(id_string, "c");
2366 /* Is this a good idea ?
2367 * Well we should do a soft reset at this point ...
2369 if (!has_compat(VT420) && has_compat(VT100)) {
2370 if (!cfg.no_remote_resize) {
2372 request_resize(132, 24);
2374 request_resize(80, 24);
2384 case 'P': /* Linux palette sequence */
2385 termstate = SEEN_OSC_P;
2388 case 'R': /* Linux palette reset */
2391 termstate = TOPLEVEL;
2393 case 'W': /* word-set */
2394 termstate = SEEN_OSC_W;
2407 esc_args[0] = 10 * esc_args[0] + c - '0';
2411 * Grotty hack to support xterm and DECterm title
2412 * sequences concurrently.
2414 if (esc_args[0] == 2) {
2418 /* else fall through */
2420 termstate = OSC_STRING;
2426 * This OSC stuff is EVIL. It takes just one character to get into
2427 * sysline mode and it's not initially obvious how to get out.
2428 * So I've added CR and LF as string aborts.
2429 * This shouldn't effect compatibility as I believe embedded
2430 * control characters are supposed to be interpreted (maybe?)
2431 * and they don't display anything useful anyway.
2435 if (c == '\n' || c == '\r') {
2436 termstate = TOPLEVEL;
2437 } else if (c == 0234 || c == '\007') {
2439 * These characters terminate the string; ST and BEL
2440 * terminate the sequence and trigger instant
2441 * processing of it, whereas ESC goes back to SEEN_ESC
2442 * mode unless it is followed by \, in which case it is
2443 * synonymous with ST in the first place.
2446 termstate = TOPLEVEL;
2447 } else if (c == '\033')
2448 termstate = OSC_MAYBE_ST;
2449 else if (osc_strlen < OSC_STR_MAX)
2450 osc_string[osc_strlen++] = c;
2454 int max = (osc_strlen == 0 ? 21 : 16);
2456 if (c >= '0' && c <= '9')
2458 else if (c >= 'A' && c <= 'A' + max - 10)
2460 else if (c >= 'a' && c <= 'a' + max - 10)
2463 termstate = TOPLEVEL;
2466 osc_string[osc_strlen++] = val;
2467 if (osc_strlen >= 7) {
2468 palette_set(osc_string[0],
2469 osc_string[1] * 16 + osc_string[2],
2470 osc_string[3] * 16 + osc_string[4],
2471 osc_string[5] * 16 + osc_string[6]);
2473 termstate = TOPLEVEL;
2489 esc_args[0] = 10 * esc_args[0] + c - '0';
2492 termstate = OSC_STRING;
2497 termstate = TOPLEVEL;
2498 seen_disp_event = TRUE;
2501 move(curs.x, curs.y - 1, 1);
2504 move(curs.x, curs.y + 1, 1);
2507 move(curs.x + 1, curs.y, 1);
2510 move(curs.x - 1, curs.y, 1);
2513 * From the VT100 Manual
2514 * NOTE: The special graphics characters in the VT100
2515 * are different from those in the VT52
2517 * From VT102 manual:
2518 * 137 _ Blank - Same
2519 * 140 ` Reserved - Humm.
2520 * 141 a Solid rectangle - Similar
2521 * 142 b 1/ - Top half of fraction for the
2522 * 143 c 3/ - subscript numbers below.
2525 * 146 f Degrees - Same
2526 * 147 g Plus or minus - Same
2528 * 151 i Ellipsis (dots)
2531 * 154 l Bar at scan 0
2532 * 155 m Bar at scan 1
2533 * 156 n Bar at scan 2
2534 * 157 o Bar at scan 3 - Similar
2535 * 160 p Bar at scan 4 - Similar
2536 * 161 q Bar at scan 5 - Similar
2537 * 162 r Bar at scan 6 - Same
2538 * 163 s Bar at scan 7 - Similar
2553 cset_attr[cset = 0] = ATTR_LINEDRW;
2556 cset_attr[cset = 0] = ATTR_ASCII;
2563 scroll(0, rows - 1, -1, TRUE);
2564 else if (curs.y > 0)
2570 erase_lots(FALSE, FALSE, TRUE);
2574 erase_lots(TRUE, FALSE, TRUE);
2578 /* XXX Print cursor line */
2581 /* XXX Start controller mode */
2584 /* XXX Stop controller mode */
2588 termstate = VT52_Y1;
2591 ldisc_send("\033/Z", 3, 0);
2594 app_keypad_keys = TRUE;
2597 app_keypad_keys = FALSE;
2600 /* XXX This should switch to VT100 mode not current or default
2601 * VT mode. But this will only have effect in a VT220+
2605 blink_is_real = cfg.blinktext;
2609 /* XXX Enter auto print mode */
2612 /* XXX Exit auto print mode */
2615 /* XXX Print screen */
2621 /* compatibility(ATARI) */
2623 erase_lots(FALSE, FALSE, TRUE);
2627 /* compatibility(ATARI) */
2628 if (curs.y <= marg_b)
2629 scroll(curs.y, marg_b, -1, FALSE);
2632 /* compatibility(ATARI) */
2633 if (curs.y <= marg_b)
2634 scroll(curs.y, marg_b, 1, TRUE);
2637 /* compatibility(ATARI) */
2638 termstate = VT52_FG;
2641 /* compatibility(ATARI) */
2642 termstate = VT52_BG;
2645 /* compatibility(ATARI) */
2646 erase_lots(FALSE, TRUE, FALSE);
2650 /* compatibility(ATARI) */
2654 /* compatibility(ATARI) */
2657 /* case 'j': Save cursor position - broken on ST */
2658 /* case 'k': Restore cursor position */
2660 /* compatibility(ATARI) */
2661 erase_lots(TRUE, TRUE, TRUE);
2667 /* compatibility(ATARI) */
2668 erase_lots(TRUE, TRUE, FALSE);
2671 /* compatibility(ATARI) */
2672 curr_attr |= ATTR_REVERSE;
2675 /* compatibility(ATARI) */
2676 curr_attr &= ~ATTR_REVERSE;
2678 case 'v': /* wrap Autowrap on - Wyse style */
2679 /* compatibility(ATARI) */
2682 case 'w': /* Autowrap off */
2683 /* compatibility(ATARI) */
2688 /* compatibility(OTHER) */
2690 curr_attr = ATTR_DEFAULT;
2692 erase_char = (' ' | ATTR_ASCII |
2694 (ATTR_FGMASK | ATTR_BGMASK)));
2697 /* compatibility(VI50) */
2698 curr_attr |= ATTR_UNDER;
2701 /* compatibility(VI50) */
2702 curr_attr &= ~ATTR_UNDER;
2705 /* compatibility(VI50) */
2707 curr_attr |= ATTR_BOLD;
2710 /* compatibility(VI50) */
2712 curr_attr &= ~ATTR_BOLD;
2718 termstate = VT52_Y2;
2719 move(curs.x, c - ' ', 0);
2722 termstate = TOPLEVEL;
2723 move(c - ' ', curs.y, 0);
2728 termstate = TOPLEVEL;
2729 curr_attr &= ~ATTR_FGMASK;
2730 curr_attr &= ~ATTR_BOLD;
2731 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2732 if ((c & 0x8) || vt52_bold)
2733 curr_attr |= ATTR_BOLD;
2736 erase_char = (' ' | ATTR_ASCII |
2737 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2740 termstate = TOPLEVEL;
2741 curr_attr &= ~ATTR_BGMASK;
2742 curr_attr &= ~ATTR_BLINK;
2743 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2745 /* Note: bold background */
2747 curr_attr |= ATTR_BLINK;
2750 erase_char = (' ' | ATTR_ASCII |
2751 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2754 default: break; /* placate gcc warning about enum use */
2756 if (selstate != NO_SELECTION) {
2757 pos cursplus = curs;
2759 check_selection(curs, cursplus);
2768 * Compare two lines to determine whether they are sufficiently
2769 * alike to scroll-optimise one to the other. Return the degree of
2772 static int linecmp(unsigned long *a, unsigned long *b)
2776 for (i = n = 0; i < cols; i++)
2777 n += (*a++ == *b++);
2783 * Given a context, update the window. Out of paranoia, we don't
2784 * allow WM_PAINT responses to do scrolling optimisations.
2786 static void do_paint(Context ctx, int may_optimise)
2788 int i, j, our_curs_y;
2789 unsigned long rv, cursor;
2792 long cursor_background = ERASE_CHAR;
2793 unsigned long ticks;
2796 * Check the visual bell state.
2799 ticks = GETTICKCOUNT();
2800 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2804 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2807 * screen array, disptop, scrtop,
2809 * cfg.blinkpc, blink_is_real, tblinker,
2810 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2813 /* Has the cursor position or type changed ? */
2816 if (blinker || !cfg.blink_cur)
2817 cursor = TATTR_ACTCURS;
2821 cursor = TATTR_PASCURS;
2823 cursor |= TATTR_RIGHTCURS;
2826 our_curs_y = curs.y - disptop;
2828 if (dispcurs && (curstype != cursor ||
2830 disptext + our_curs_y * (cols + 1) + curs.x)) {
2831 if (dispcurs > disptext &&
2832 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2833 dispcurs[-1] |= ATTR_INVALID;
2834 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2835 dispcurs[1] |= ATTR_INVALID;
2836 *dispcurs |= ATTR_INVALID;
2841 /* The normal screen data */
2842 for (i = 0; i < rows; i++) {
2843 unsigned long *ldata;
2845 int idx, dirty_line, dirty_run, selected;
2846 unsigned long attr = 0;
2847 int updated_line = 0;
2850 int last_run_dirty = 0;
2852 scrpos.y = i + disptop;
2853 ldata = lineptr(scrpos.y);
2854 lattr = (ldata[cols] & LATTR_MODE);
2856 idx = i * (cols + 1);
2857 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2858 disptext[idx + cols] = ldata[cols];
2860 for (j = 0; j < cols; j++, idx++) {
2861 unsigned long tattr, tchar;
2862 unsigned long *d = ldata + j;
2866 tchar = (*d & (CHAR_MASK | CSET_MASK));
2867 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2868 switch (tchar & CSET_MASK) {
2870 tchar = unitab_line[tchar & 0xFF];
2873 tchar = unitab_xterm[tchar & 0xFF];
2876 tchar = unitab_scoacs[tchar&0xFF];
2879 tattr |= (tchar & CSET_MASK);
2881 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2884 /* Video reversing things */
2885 if (seltype == LEXICOGRAPHIC)
2886 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2888 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2890 ^ (selected ? ATTR_REVERSE : 0));
2892 /* 'Real' blinking ? */
2893 if (blink_is_real && (tattr & ATTR_BLINK)) {
2894 if (has_focus && tblinker) {
2896 tattr &= ~CSET_MASK;
2899 tattr &= ~ATTR_BLINK;
2903 * Check the font we'll _probably_ be using to see if
2904 * the character is wide when we don't want it to be.
2906 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2907 if ((tattr & ATTR_WIDE) == 0 &&
2908 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2909 tattr |= ATTR_NARROW;
2910 } else if (disptext[idx]&ATTR_NARROW)
2911 tattr |= ATTR_NARROW;
2913 /* Cursor here ? Save the 'background' */
2914 if (i == our_curs_y && j == curs.x) {
2915 cursor_background = tattr | tchar;
2916 dispcurs = disptext + idx;
2919 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2922 break_run = (tattr != attr || j - start >= sizeof(ch));
2924 /* Special hack for VT100 Linedraw glyphs */
2925 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2926 && tchar <= 0xBD) break_run = TRUE;
2928 if (!dbcs_screenfont && !dirty_line) {
2929 if ((tchar | tattr) == disptext[idx])
2931 else if (!dirty_run && ccount == 1)
2936 if ((dirty_run || last_run_dirty) && ccount > 0) {
2937 do_text(ctx, start, i, ch, ccount, attr, lattr);
2943 if (dbcs_screenfont)
2944 last_run_dirty = dirty_run;
2945 dirty_run = dirty_line;
2948 if ((tchar | tattr) != disptext[idx])
2950 ch[ccount++] = (char) tchar;
2951 disptext[idx] = tchar | tattr;
2953 /* If it's a wide char step along to the next one. */
2954 if (tattr & ATTR_WIDE) {
2958 /* Cursor is here ? Ouch! */
2959 if (i == our_curs_y && j == curs.x) {
2960 cursor_background = *d;
2961 dispcurs = disptext + idx;
2963 if (disptext[idx] != *d)
2969 if (dirty_run && ccount > 0) {
2970 do_text(ctx, start, i, ch, ccount, attr, lattr);
2974 /* Cursor on this line ? (and changed) */
2975 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2976 ch[0] = (char) (cursor_background & CHAR_MASK);
2977 attr = (cursor_background & ATTR_MASK) | cursor;
2978 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2985 * Flick the switch that says if blinking things should be shown or hidden.
2988 void term_blink(int flg)
2990 static long last_blink = 0;
2991 static long last_tblink = 0;
2992 long now, blink_diff;
2994 now = GETTICKCOUNT();
2995 blink_diff = now - last_tblink;
2997 /* Make sure the text blinks no more than 2Hz */
2998 if (blink_diff < 0 || blink_diff > 450) {
3000 tblinker = !tblinker;
3009 blink_diff = now - last_blink;
3011 /* Make sure the cursor blinks no faster than system blink rate */
3012 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3020 * Invalidate the whole screen so it will be repainted in full.
3022 void term_invalidate(void)
3026 for (i = 0; i < rows * (cols + 1); i++)
3027 disptext[i] = ATTR_INVALID;
3031 * Paint the window in response to a WM_PAINT message.
3033 void term_paint(Context ctx, int left, int top, int right, int bottom)
3036 if (left < 0) left = 0;
3037 if (top < 0) top = 0;
3038 if (right >= cols) right = cols-1;
3039 if (bottom >= rows) bottom = rows-1;
3041 for (i = top; i <= bottom && i < rows; i++) {
3042 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
3043 for (j = left; j <= right && j < cols; j++)
3044 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3046 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
3047 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3050 /* This should happen soon enough, also for some reason it sometimes
3051 * fails to actually do anything when re-sizing ... painting the wrong
3055 do_paint (ctx, FALSE);
3059 * Attempt to scroll the scrollback. The second parameter gives the
3060 * position we want to scroll to; the first is +1 to denote that
3061 * this position is relative to the beginning of the scrollback, -1
3062 * to denote it is relative to the end, and 0 to denote that it is
3063 * relative to the current position.
3065 void term_scroll(int rel, int where)
3067 int sbtop = -count234(scrollback);
3069 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
3070 if (disptop < sbtop)
3078 static void clipme(pos top, pos bottom, int rect)
3081 wchar_t *wbptr; /* where next char goes within workbuf */
3083 int wblen = 0; /* workbuf len */
3084 int buflen; /* amount of memory allocated to workbuf */
3086 buflen = 5120; /* Default size */
3087 workbuf = smalloc(buflen * sizeof(wchar_t));
3088 wbptr = workbuf; /* start filling here */
3089 old_top_x = top.x; /* needed for rect==1 */
3091 while (poslt(top, bottom)) {
3093 unsigned long *ldata = lineptr(top.y);
3097 * nlpos will point at the maximum position on this line we
3098 * should copy up to. So we start it at the end of the
3105 * ... move it backwards if there's unused space at the end
3106 * of the line (and also set `nl' if this is the case,
3107 * because in normal selection mode this means we need a
3108 * newline at the end)...
3110 if (!(ldata[cols] & LATTR_WRAPPED)) {
3111 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3112 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3113 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3114 && poslt(top, nlpos))
3116 if (poslt(nlpos, bottom))
3121 * ... and then clip it to the terminal x coordinate if
3122 * we're doing rectangular selection. (In this case we
3123 * still did the above, so that copying e.g. the right-hand
3124 * column from a table doesn't fill with spaces on the
3128 if (nlpos.x > bottom.x)
3130 nl = (top.y < bottom.y);
3133 while (poslt(top, bottom) && poslt(top, nlpos)) {
3136 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3138 wchar_t cbuf[16], *p;
3139 int uc = (ldata[top.x] & 0xFFFF);
3142 if (uc == UCSWIDE) {
3147 switch (uc & CSET_MASK) {
3150 uc = unitab_xterm[uc & 0xFF];
3154 uc = unitab_line[uc & 0xFF];
3157 uc = unitab_scoacs[uc&0xFF];
3160 switch (uc & CSET_MASK) {
3162 uc = unitab_font[uc & 0xFF];
3165 uc = unitab_oemcp[uc & 0xFF];
3169 set = (uc & CSET_MASK);
3170 c = (uc & CHAR_MASK);
3174 if (DIRECT_FONT(uc)) {
3175 if (c >= ' ' && c != 0x7F) {
3176 unsigned char buf[4];
3179 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
3181 buf[1] = (unsigned char) ldata[top.x + 1];
3182 rv = MultiByteToWideChar(font_codepage,
3183 0, buf, 2, wbuf, 4);
3187 rv = MultiByteToWideChar(font_codepage,
3188 0, buf, 1, wbuf, 4);
3192 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3199 for (p = cbuf; *p; p++) {
3200 /* Enough overhead for trailing NL and nul */
3201 if (wblen >= buflen - 16) {
3204 sizeof(wchar_t) * (buflen += 100));
3205 wbptr = workbuf + wblen;
3214 for (i = 0; i < sel_nl_sz; i++) {
3216 *wbptr++ = sel_nl[i];
3220 top.x = rect ? old_top_x : 0;
3224 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3225 if (buflen > 0) /* indicates we allocated this buffer */
3229 void term_copyall(void)
3232 top.y = -count234(scrollback);
3234 clipme(top, curs, 0);
3238 * The wordness array is mainly for deciding the disposition of the US-ASCII
3241 static int wordtype(int uc)
3244 int start, end, ctype;
3245 } *wptr, ucs_words[] = {
3251 0x037e, 0x037e, 1}, /* Greek question mark */
3253 0x0387, 0x0387, 1}, /* Greek ano teleia */
3255 0x055a, 0x055f, 1}, /* Armenian punctuation */
3257 0x0589, 0x0589, 1}, /* Armenian full stop */
3259 0x0700, 0x070d, 1}, /* Syriac punctuation */
3261 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3263 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3265 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3267 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3269 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3271 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3273 0x2000, 0x200a, 0}, /* Various spaces */
3275 0x2070, 0x207f, 2}, /* superscript */
3277 0x2080, 0x208f, 2}, /* subscript */
3279 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3281 0x3000, 0x3000, 0}, /* ideographic space */
3283 0x3001, 0x3020, 1}, /* ideographic punctuation */
3285 0x303f, 0x309f, 3}, /* Hiragana */
3287 0x30a0, 0x30ff, 3}, /* Katakana */
3289 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3291 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3293 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3295 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3297 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3299 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3301 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3303 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3305 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3310 uc &= (CSET_MASK | CHAR_MASK);
3312 switch (uc & CSET_MASK) {
3314 uc = unitab_xterm[uc & 0xFF];
3317 uc = unitab_line[uc & 0xFF];
3320 uc = unitab_scoacs[uc&0xFF];
3323 switch (uc & CSET_MASK) {
3325 uc = unitab_font[uc & 0xFF];
3328 uc = unitab_oemcp[uc & 0xFF];
3332 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3333 * fail as there's such a thing as a double width space. :-(
3335 if (dbcs_screenfont && font_codepage == line_codepage)
3339 return wordness[uc];
3341 for (wptr = ucs_words; wptr->start; wptr++) {
3342 if (uc >= wptr->start && uc <= wptr->end)
3350 * Spread the selection outwards according to the selection mode.
3352 static pos sel_spread_half(pos p, int dir)
3354 unsigned long *ldata;
3356 int topy = -count234(scrollback);
3358 ldata = lineptr(p.y);
3363 * In this mode, every character is a separate unit, except
3364 * for runs of spaces at the end of a non-wrapping line.
3366 if (!(ldata[cols] & LATTR_WRAPPED)) {
3367 unsigned long *q = ldata + cols;
3368 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3370 if (q == ldata + cols)
3372 if (p.x >= q - ldata)
3373 p.x = (dir == -1 ? q - ldata : cols - 1);
3378 * In this mode, the units are maximal runs of characters
3379 * whose `wordness' has the same value.
3381 wvalue = wordtype(ldata[p.x]);
3385 if (wordtype(ldata[p.x + 1]) == wvalue)
3390 if (ldata[cols] & LATTR_WRAPPED) {
3391 unsigned long *ldata2;
3392 ldata2 = lineptr(p.y+1);
3393 if (wordtype(ldata2[0]) == wvalue) {
3406 if (wordtype(ldata[p.x - 1]) == wvalue)
3411 unsigned long *ldata2;
3414 ldata2 = lineptr(p.y-1);
3415 if ((ldata2[cols] & LATTR_WRAPPED) &&
3416 wordtype(ldata2[cols-1]) == wvalue) {
3428 * In this mode, every line is a unit.
3430 p.x = (dir == -1 ? 0 : cols - 1);
3436 static void sel_spread(void)
3438 if (seltype == LEXICOGRAPHIC) {
3439 selstart = sel_spread_half(selstart, -1);
3441 selend = sel_spread_half(selend, +1);
3446 void term_do_paste(void)
3451 get_clip(&data, &len);
3456 sfree(paste_buffer);
3457 paste_pos = paste_hold = paste_len = 0;
3458 paste_buffer = smalloc(len * sizeof(wchar_t));
3461 while (p < data + len) {
3462 while (p < data + len &&
3463 !(p <= data + len - sel_nl_sz &&
3464 !memcmp(p, sel_nl, sizeof(sel_nl))))
3469 for (i = 0; i < p - q; i++) {
3470 paste_buffer[paste_len++] = q[i];
3474 if (p <= data + len - sel_nl_sz &&
3475 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3476 paste_buffer[paste_len++] = '\r';
3482 /* Assume a small paste will be OK in one go. */
3483 if (paste_len < 256) {
3484 luni_send(paste_buffer, paste_len, 0);
3486 sfree(paste_buffer);
3488 paste_pos = paste_hold = paste_len = 0;
3491 get_clip(NULL, NULL);
3494 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3495 int shift, int ctrl, int alt)
3498 unsigned long *ldata;
3499 int raw_mouse = (xterm_mouse &&
3500 !cfg.no_mouse_rep &&
3501 !(cfg.mouse_override && shift));
3502 int default_seltype;
3506 if (a == MA_DRAG && !raw_mouse)
3511 if (a == MA_DRAG && !raw_mouse)
3524 selpoint.y = y + disptop;
3526 ldata = lineptr(selpoint.y);
3527 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3531 int encstate = 0, r, c;
3533 static int is_down = 0;
3537 encstate = 0x20; /* left button down */
3548 case MBT_WHEEL_DOWN:
3551 default: break; /* placate gcc warning about enum use */
3555 if (xterm_mouse == 1)
3568 default: break; /* placate gcc warning about enum use */
3577 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3578 ldisc_send(abuf, 6, 0);
3582 b = translate_button(b);
3585 * Set the selection type (rectangular or normal) at the start
3586 * of a selection attempt, from the state of Alt.
3588 if (!alt ^ !cfg.rect_select)
3589 default_seltype = RECTANGULAR;
3591 default_seltype = LEXICOGRAPHIC;
3593 if (selstate == NO_SELECTION) {
3594 seltype = default_seltype;
3597 if (b == MBT_SELECT && a == MA_CLICK) {
3599 selstate = ABOUT_TO;
3600 seltype = default_seltype;
3601 selanchor = selpoint;
3603 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3605 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3606 selstate = DRAGGING;
3607 selstart = selanchor = selpoint;
3611 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3612 (b == MBT_EXTEND && a != MA_RELEASE)) {
3613 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3615 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3616 if (seltype == LEXICOGRAPHIC) {
3618 * For normal selection, we extend by moving
3619 * whichever end of the current selection is closer
3622 if (posdiff(selpoint, selstart) <
3623 posdiff(selend, selstart) / 2) {
3627 selanchor = selstart;
3631 * For rectangular selection, we have a choice of
3632 * _four_ places to put selanchor and selpoint: the
3633 * four corners of the selection.
3635 if (2*selpoint.x < selstart.x + selend.x)
3636 selanchor.x = selend.x-1;
3638 selanchor.x = selstart.x;
3640 if (2*selpoint.y < selstart.y + selend.y)
3641 selanchor.y = selend.y;
3643 selanchor.y = selstart.y;
3645 selstate = DRAGGING;
3647 if (selstate != ABOUT_TO && selstate != DRAGGING)
3648 selanchor = selpoint;
3649 selstate = DRAGGING;
3650 if (seltype == LEXICOGRAPHIC) {
3652 * For normal selection, we set (selstart,selend) to
3653 * (selpoint,selanchor) in some order.
3655 if (poslt(selpoint, selanchor)) {
3656 selstart = selpoint;
3660 selstart = selanchor;
3666 * For rectangular selection, we may need to
3667 * interchange x and y coordinates (if the user has
3668 * dragged in the -x and +y directions, or vice versa).
3670 selstart.x = min(selanchor.x, selpoint.x);
3671 selend.x = 1+max(selanchor.x, selpoint.x);
3672 selstart.y = min(selanchor.y, selpoint.y);
3673 selend.y = max(selanchor.y, selpoint.y);
3676 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3677 if (selstate == DRAGGING) {
3679 * We've completed a selection. We now transfer the
3680 * data to the clipboard.
3682 clipme(selstart, selend, (seltype == RECTANGULAR));
3683 selstate = SELECTED;
3685 selstate = NO_SELECTION;
3686 } else if (b == MBT_PASTE
3687 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3698 sfree(paste_buffer);
3705 static long last_paste = 0;
3706 long now, paste_diff;
3711 /* Don't wait forever to paste */
3713 now = GETTICKCOUNT();
3714 paste_diff = now - last_paste;
3715 if (paste_diff >= 0 && paste_diff < 450)
3720 while (paste_pos < paste_len) {
3722 while (n + paste_pos < paste_len) {
3723 if (paste_buffer[paste_pos + n++] == '\r')
3726 luni_send(paste_buffer + paste_pos, n, 0);
3729 if (paste_pos < paste_len) {
3734 sfree(paste_buffer);
3739 static void deselect(void)
3741 selstate = NO_SELECTION;
3742 selstart.x = selstart.y = selend.x = selend.y = 0;
3745 void term_deselect(void)
3751 int term_ldisc(int option)
3753 if (option == LD_ECHO)
3754 return term_echoing;
3755 if (option == LD_EDIT)
3756 return term_editing;
3761 * from_backend(), to get data from the backend for the terminal.
3763 int from_backend(int is_stderr, char *data, int len)
3767 bufchain_add(&inbuf, data, len);
3770 * term_out() always completely empties inbuf. Therefore,
3771 * there's no reason at all to return anything other than zero
3772 * from this function, because there _can't_ be a question of
3773 * the remote side needing to wait until term_out() has cleared
3776 * This is a slightly suboptimal way to deal with SSH2 - in
3777 * principle, the window mechanism would allow us to continue
3778 * to accept data on forwarded ports and X connections even
3779 * while the terminal processing was going slowly - but we
3780 * can't do the 100% right thing without moving the terminal
3781 * processing into a separate thread, and that might hurt
3782 * portability. So we manage stdout buffering the old SSH1 way:
3783 * if the terminal processing goes slowly, the whole SSH
3784 * connection stops accepting data until it's ready.
3786 * In practice, I can't imagine this causing serious trouble.