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)
1045 if (!printing && !only_printing)
1046 return; /* we need do nothing */
1049 while ((size = bufchain_size(&printer_buf)) > 0) {
1050 bufchain_prefix(&printer_buf, &data, &len);
1052 if (c == '\033' || c == '\233') {
1053 bufchain_consume(&printer_buf, size);
1056 printer_job_data(print_job, &c, 1);
1057 bufchain_consume(&printer_buf, 1);
1060 printer_finish_job(print_job);
1062 printing = only_printing = FALSE;
1066 * Remove everything currently in `inbuf' and stick it up on the
1067 * in-memory display. There's a big state machine in here to
1068 * process escape sequences...
1073 unsigned char localbuf[256], *chars;
1078 chars = NULL; /* placate compiler warnings */
1079 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1083 bufchain_prefix(&inbuf, &ret, &nchars);
1084 if (nchars > sizeof(localbuf))
1085 nchars = sizeof(localbuf);
1086 memcpy(localbuf, ret, nchars);
1087 bufchain_consume(&inbuf, nchars);
1089 assert(chars != NULL);
1095 * Optionally log the session traffic to a file. Useful for
1096 * debugging and possibly also useful for actual logging.
1098 if (cfg.logtype == LGTYP_DEBUG)
1099 logtraffic((unsigned char) c, LGTYP_DEBUG);
1105 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1106 * be able to display 8-bit characters, but I'll let that go 'cause
1111 * If we're printing, add the character to the printer
1115 bufchain_add(&printer_buf, &c, 1);
1118 * If we're in print-only mode, we use a much simpler
1119 * state machine designed only to recognise the ESC[4i
1120 * termination sequence.
1122 if (only_printing) {
1125 else if (c == (unsigned char)'\233')
1127 else if (c == '[' && print_state == 1)
1129 else if (c == '4' && print_state == 2)
1131 else if (c == 'i' && print_state == 3)
1135 if (print_state == 4) {
1136 printing = only_printing = FALSE;
1137 term_print_finish();
1143 /* First see about all those translations. */
1144 if (termstate == TOPLEVEL) {
1146 switch (utf_state) {
1149 /* UTF-8 must be stateless so we ignore iso2022. */
1150 if (unitab_ctrl[c] != 0xFF)
1152 else c = ((unsigned char)c) | ATTR_ASCII;
1154 } else if ((c & 0xe0) == 0xc0) {
1155 utf_size = utf_state = 1;
1156 utf_char = (c & 0x1f);
1157 } else if ((c & 0xf0) == 0xe0) {
1158 utf_size = utf_state = 2;
1159 utf_char = (c & 0x0f);
1160 } else if ((c & 0xf8) == 0xf0) {
1161 utf_size = utf_state = 3;
1162 utf_char = (c & 0x07);
1163 } else if ((c & 0xfc) == 0xf8) {
1164 utf_size = utf_state = 4;
1165 utf_char = (c & 0x03);
1166 } else if ((c & 0xfe) == 0xfc) {
1167 utf_size = utf_state = 5;
1168 utf_char = (c & 0x01);
1179 if ((c & 0xC0) != 0x80) {
1185 utf_char = (utf_char << 6) | (c & 0x3f);
1191 /* Is somebody trying to be evil! */
1193 (c < 0x800 && utf_size >= 2) ||
1194 (c < 0x10000 && utf_size >= 3) ||
1195 (c < 0x200000 && utf_size >= 4) ||
1196 (c < 0x4000000 && utf_size >= 5))
1199 /* Unicode line separator and paragraph separator are CR-LF */
1200 if (c == 0x2028 || c == 0x2029)
1203 /* High controls are probably a Baaad idea too. */
1207 /* The UTF-16 surrogates are not nice either. */
1208 /* The standard give the option of decoding these:
1209 * I don't want to! */
1210 if (c >= 0xD800 && c < 0xE000)
1213 /* ISO 10646 characters now limited to UTF-16 range. */
1217 /* This is currently a TagPhobic application.. */
1218 if (c >= 0xE0000 && c <= 0xE007F)
1221 /* U+FEFF is best seen as a null. */
1224 /* But U+FFFE is an error. */
1225 if (c == 0xFFFE || c == 0xFFFF)
1228 /* Oops this is a 16bit implementation */
1233 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1235 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1237 if (sco_acs == 2) c ^= 0x80;
1240 switch (cset_attr[cset]) {
1242 * Linedraw characters are different from 'ESC ( B'
1243 * only for a small range. For ones outside that
1244 * range, make sure we use the same font as well as
1245 * the same encoding.
1248 if (unitab_ctrl[c] != 0xFF)
1251 c = ((unsigned char) c) | ATTR_LINEDRW;
1255 /* If UK-ASCII, make the '#' a LineDraw Pound */
1257 c = '}' | ATTR_LINEDRW;
1260 /*FALLTHROUGH*/ case ATTR_ASCII:
1261 if (unitab_ctrl[c] != 0xFF)
1264 c = ((unsigned char) c) | ATTR_ASCII;
1267 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1273 /* How about C1 controls ? */
1274 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1275 has_compat(VT220)) {
1276 termstate = SEEN_ESC;
1278 c = '@' + (c & 0x1F);
1281 /* Or the GL control. */
1282 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1283 if (curs.x && !wrapnext)
1287 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1288 *cpos = (' ' | curr_attr | ATTR_ASCII);
1290 /* Or normal C0 controls. */
1291 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1293 case '\005': /* terminal type query */
1294 /* Strictly speaking this is VT100 but a VT100 defaults to
1295 * no response. Other terminals respond at their option.
1297 * Don't put a CR in the default string as this tends to
1298 * upset some weird software.
1300 * An xterm returns "xterm" (5 characters)
1302 compatibility(ANSIMIN);
1304 char abuf[256], *s, *d;
1306 for (s = cfg.answerback, d = abuf; *s; s++) {
1308 if (*s >= 'a' && *s <= 'z')
1309 *d++ = (*s - ('a' - 1));
1310 else if ((*s >= '@' && *s <= '_') ||
1311 *s == '?' || (*s & 0x80))
1316 } else if (*s == '^') {
1321 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1326 struct beeptime *newbeep;
1327 unsigned long ticks;
1329 ticks = GETTICKCOUNT();
1331 if (!beep_overloaded) {
1332 newbeep = smalloc(sizeof(struct beeptime));
1333 newbeep->ticks = ticks;
1334 newbeep->next = NULL;
1338 beeptail->next = newbeep;
1344 * Throw out any beeps that happened more than
1348 beephead->ticks < ticks - cfg.bellovl_t) {
1349 struct beeptime *tmp = beephead;
1350 beephead = tmp->next;
1357 if (cfg.bellovl && beep_overloaded &&
1358 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1360 * If we're currently overloaded and the
1361 * last beep was more than s seconds ago,
1362 * leave overload mode.
1364 beep_overloaded = FALSE;
1365 } else if (cfg.bellovl && !beep_overloaded &&
1366 nbeeps >= cfg.bellovl_n) {
1368 * Now, if we have n or more beeps
1369 * remaining in the queue, go into overload
1372 beep_overloaded = TRUE;
1377 * Perform an actual beep if we're not overloaded.
1379 if (!cfg.bellovl || !beep_overloaded) {
1381 if (cfg.beep == BELL_VISUAL) {
1383 vbell_startpoint = ticks;
1391 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1392 else if (curs.x == 0 && curs.y > 0)
1393 curs.x = cols - 1, curs.y--;
1399 seen_disp_event = TRUE;
1402 compatibility(VT100);
1406 compatibility(VT100);
1411 termstate = VT52_ESC;
1413 compatibility(ANSIMIN);
1414 termstate = SEEN_ESC;
1422 seen_disp_event = TRUE;
1424 logtraffic((unsigned char) c, LGTYP_ASCII);
1427 if (has_compat(SCOANSI)) {
1429 erase_lots(FALSE, FALSE, TRUE);
1432 seen_disp_event = 1;
1436 compatibility(VT100);
1438 if (curs.y == marg_b)
1439 scroll(marg_t, marg_b, 1, TRUE);
1440 else if (curs.y < rows - 1)
1446 seen_disp_event = 1;
1448 logtraffic((unsigned char) c, LGTYP_ASCII);
1452 pos old_curs = curs;
1453 unsigned long *ldata = lineptr(curs.y);
1457 } while (curs.x < cols - 1 && !tabs[curs.x]);
1459 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1460 if (curs.x >= cols / 2)
1461 curs.x = cols / 2 - 1;
1468 check_selection(old_curs, curs);
1470 seen_disp_event = TRUE;
1474 switch (termstate) {
1476 /* Only graphic characters get this far, ctrls are stripped above */
1477 if (wrapnext && wrap) {
1478 cpos[1] |= LATTR_WRAPPED;
1479 if (curs.y == marg_b)
1480 scroll(marg_t, marg_b, 1, TRUE);
1481 else if (curs.y < rows - 1)
1489 if (selstate != NO_SELECTION) {
1490 pos cursplus = curs;
1492 check_selection(curs, cursplus);
1494 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1495 logtraffic((unsigned char) c, LGTYP_ASCII);
1497 extern int wcwidth(wchar_t ucs);
1502 width = wcwidth((wchar_t) c);
1505 *cpos++ = c | curr_attr;
1506 if (++curs.x == cols) {
1507 *cpos |= LATTR_WRAPPED;
1508 if (curs.y == marg_b)
1509 scroll(marg_t, marg_b, 1, TRUE);
1510 else if (curs.y < rows - 1)
1515 *cpos++ = UCSWIDE | curr_attr;
1518 *cpos++ = c | curr_attr;
1525 if (curs.x == cols) {
1529 if (wrap && vt52_mode) {
1530 cpos[1] |= LATTR_WRAPPED;
1531 if (curs.y == marg_b)
1532 scroll(marg_t, marg_b, 1, TRUE);
1533 else if (curs.y < rows - 1)
1540 seen_disp_event = 1;
1545 * This state is virtually identical to SEEN_ESC, with the
1546 * exception that we have an OSC sequence in the pipeline,
1547 * and _if_ we see a backslash, we process it.
1551 termstate = TOPLEVEL;
1554 /* else fall through */
1556 if (c >= ' ' && c <= '/') {
1563 termstate = TOPLEVEL;
1564 switch (ANSI(c, esc_query)) {
1565 case '[': /* enter CSI mode */
1566 termstate = SEEN_CSI;
1568 esc_args[0] = ARG_DEFAULT;
1571 case ']': /* xterm escape sequences */
1572 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1573 compatibility(OTHER);
1574 termstate = SEEN_OSC;
1577 case '7': /* save cursor */
1578 compatibility(VT100);
1581 case '8': /* restore cursor */
1582 compatibility(VT100);
1584 seen_disp_event = TRUE;
1587 compatibility(VT100);
1588 app_keypad_keys = TRUE;
1591 compatibility(VT100);
1592 app_keypad_keys = FALSE;
1594 case 'D': /* exactly equivalent to LF */
1595 compatibility(VT100);
1596 if (curs.y == marg_b)
1597 scroll(marg_t, marg_b, 1, TRUE);
1598 else if (curs.y < rows - 1)
1602 seen_disp_event = TRUE;
1604 case 'E': /* exactly equivalent to CR-LF */
1605 compatibility(VT100);
1607 if (curs.y == marg_b)
1608 scroll(marg_t, marg_b, 1, TRUE);
1609 else if (curs.y < rows - 1)
1613 seen_disp_event = TRUE;
1615 case 'M': /* reverse index - backwards LF */
1616 compatibility(VT100);
1617 if (curs.y == marg_t)
1618 scroll(marg_t, marg_b, -1, TRUE);
1619 else if (curs.y > 0)
1623 seen_disp_event = TRUE;
1625 case 'Z': /* terminal type query */
1626 compatibility(VT100);
1627 ldisc_send(id_string, strlen(id_string), 0);
1629 case 'c': /* restore power-on settings */
1630 compatibility(VT100);
1633 if (!cfg.no_remote_resize)
1634 request_resize(80, rows);
1639 seen_disp_event = TRUE;
1641 case 'H': /* set a tab */
1642 compatibility(VT100);
1643 tabs[curs.x] = TRUE;
1646 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1647 compatibility(VT100);
1649 unsigned long *ldata;
1653 for (i = 0; i < rows; i++) {
1655 for (j = 0; j < cols; j++)
1656 ldata[j] = ATTR_DEFAULT | 'E';
1660 seen_disp_event = TRUE;
1661 scrtop.x = scrtop.y = 0;
1664 check_selection(scrtop, scrbot);
1668 case ANSI('3', '#'):
1669 case ANSI('4', '#'):
1670 case ANSI('5', '#'):
1671 case ANSI('6', '#'):
1672 compatibility(VT100);
1674 unsigned long nlattr;
1675 unsigned long *ldata;
1676 switch (ANSI(c, esc_query)) {
1677 case ANSI('3', '#'):
1680 case ANSI('4', '#'):
1683 case ANSI('5', '#'):
1684 nlattr = LATTR_NORM;
1686 default: /* spiritually case ANSI('6', '#'): */
1687 nlattr = LATTR_WIDE;
1690 ldata = lineptr(curs.y);
1691 ldata[cols] &= ~LATTR_MODE;
1692 ldata[cols] |= nlattr;
1696 case ANSI('A', '('):
1697 compatibility(VT100);
1698 if (!cfg.no_remote_charset)
1699 cset_attr[0] = ATTR_GBCHR;
1701 case ANSI('B', '('):
1702 compatibility(VT100);
1703 if (!cfg.no_remote_charset)
1704 cset_attr[0] = ATTR_ASCII;
1706 case ANSI('0', '('):
1707 compatibility(VT100);
1708 if (!cfg.no_remote_charset)
1709 cset_attr[0] = ATTR_LINEDRW;
1711 case ANSI('U', '('):
1712 compatibility(OTHER);
1713 if (!cfg.no_remote_charset)
1714 cset_attr[0] = ATTR_SCOACS;
1717 case ANSI('A', ')'):
1718 compatibility(VT100);
1719 if (!cfg.no_remote_charset)
1720 cset_attr[1] = ATTR_GBCHR;
1722 case ANSI('B', ')'):
1723 compatibility(VT100);
1724 if (!cfg.no_remote_charset)
1725 cset_attr[1] = ATTR_ASCII;
1727 case ANSI('0', ')'):
1728 compatibility(VT100);
1729 if (!cfg.no_remote_charset)
1730 cset_attr[1] = ATTR_LINEDRW;
1732 case ANSI('U', ')'):
1733 compatibility(OTHER);
1734 if (!cfg.no_remote_charset)
1735 cset_attr[1] = ATTR_SCOACS;
1738 case ANSI('8', '%'): /* Old Linux code */
1739 case ANSI('G', '%'):
1740 compatibility(OTHER);
1741 if (!cfg.no_remote_charset)
1744 case ANSI('@', '%'):
1745 compatibility(OTHER);
1746 if (!cfg.no_remote_charset)
1752 termstate = TOPLEVEL; /* default */
1754 if (esc_nargs <= ARGS_MAX) {
1755 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1756 esc_args[esc_nargs - 1] = 0;
1757 esc_args[esc_nargs - 1] =
1758 10 * esc_args[esc_nargs - 1] + c - '0';
1760 termstate = SEEN_CSI;
1761 } else if (c == ';') {
1762 if (++esc_nargs <= ARGS_MAX)
1763 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1764 termstate = SEEN_CSI;
1765 } else if (c < '@') {
1772 termstate = SEEN_CSI;
1774 switch (ANSI(c, esc_query)) {
1775 case 'A': /* move up N lines */
1776 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1777 seen_disp_event = TRUE;
1779 case 'e': /* move down N lines */
1780 compatibility(ANSI);
1783 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1784 seen_disp_event = TRUE;
1786 case ANSI('c', '>'): /* report xterm version */
1787 compatibility(OTHER);
1788 /* this reports xterm version 136 so that VIM can
1789 use the drag messages from the mouse reporting */
1790 ldisc_send("\033[>0;136;0c", 11, 0);
1792 case 'a': /* move right N cols */
1793 compatibility(ANSI);
1796 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1797 seen_disp_event = TRUE;
1799 case 'D': /* move left N cols */
1800 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1801 seen_disp_event = TRUE;
1803 case 'E': /* move down N lines and CR */
1804 compatibility(ANSI);
1805 move(0, curs.y + def(esc_args[0], 1), 1);
1806 seen_disp_event = TRUE;
1808 case 'F': /* move up N lines and CR */
1809 compatibility(ANSI);
1810 move(0, curs.y - def(esc_args[0], 1), 1);
1811 seen_disp_event = TRUE;
1814 case '`': /* set horizontal posn */
1815 compatibility(ANSI);
1816 move(def(esc_args[0], 1) - 1, curs.y, 0);
1817 seen_disp_event = TRUE;
1819 case 'd': /* set vertical posn */
1820 compatibility(ANSI);
1822 (dec_om ? marg_t : 0) + def(esc_args[0],
1825 seen_disp_event = TRUE;
1828 case 'f': /* set horz and vert posns at once */
1830 esc_args[1] = ARG_DEFAULT;
1831 move(def(esc_args[1], 1) - 1,
1832 (dec_om ? marg_t : 0) + def(esc_args[0],
1835 seen_disp_event = TRUE;
1837 case 'J': /* erase screen or parts of it */
1839 unsigned int i = def(esc_args[0], 0) + 1;
1842 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1845 seen_disp_event = TRUE;
1847 case 'K': /* erase line or parts of it */
1849 unsigned int i = def(esc_args[0], 0) + 1;
1852 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1854 seen_disp_event = TRUE;
1856 case 'L': /* insert lines */
1857 compatibility(VT102);
1858 if (curs.y <= marg_b)
1859 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1862 seen_disp_event = TRUE;
1864 case 'M': /* delete lines */
1865 compatibility(VT102);
1866 if (curs.y <= marg_b)
1867 scroll(curs.y, marg_b, def(esc_args[0], 1),
1870 seen_disp_event = TRUE;
1872 case '@': /* insert chars */
1873 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1874 compatibility(VT102);
1875 insch(def(esc_args[0], 1));
1876 seen_disp_event = TRUE;
1878 case 'P': /* delete chars */
1879 compatibility(VT102);
1880 insch(-def(esc_args[0], 1));
1881 seen_disp_event = TRUE;
1883 case 'c': /* terminal type query */
1884 compatibility(VT100);
1885 /* This is the response for a VT102 */
1886 ldisc_send(id_string, strlen(id_string), 0);
1888 case 'n': /* cursor position query */
1889 if (esc_args[0] == 6) {
1891 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1893 ldisc_send(buf, strlen(buf), 0);
1894 } else if (esc_args[0] == 5) {
1895 ldisc_send("\033[0n", 4, 0);
1898 case 'h': /* toggle modes to high */
1900 compatibility(VT100);
1903 for (i = 0; i < esc_nargs; i++)
1904 toggle_mode(esc_args[i], esc_query, TRUE);
1909 compatibility(VT100);
1911 if (esc_nargs != 1) break;
1912 if (esc_args[0] == 5 && *cfg.printer) {
1914 only_printing = !esc_query;
1917 } else if (esc_args[0] == 4 && printing) {
1919 only_printing = FALSE;
1920 term_print_finish();
1924 case 'l': /* toggle modes to low */
1926 compatibility(VT100);
1929 for (i = 0; i < esc_nargs; i++)
1930 toggle_mode(esc_args[i], esc_query, FALSE);
1933 case 'g': /* clear tabs */
1934 compatibility(VT100);
1935 if (esc_nargs == 1) {
1936 if (esc_args[0] == 0) {
1937 tabs[curs.x] = FALSE;
1938 } else if (esc_args[0] == 3) {
1940 for (i = 0; i < cols; i++)
1945 case 'r': /* set scroll margins */
1946 compatibility(VT100);
1947 if (esc_nargs <= 2) {
1949 top = def(esc_args[0], 1) - 1;
1950 bot = (esc_nargs <= 1
1952 0 ? rows : def(esc_args[1], rows)) - 1;
1955 /* VTTEST Bug 9 - if region is less than 2 lines
1956 * don't change region.
1958 if (bot - top > 0) {
1963 * I used to think the cursor should be
1964 * placed at the top of the newly marginned
1965 * area. Apparently not: VMS TPU falls over
1968 * Well actually it should for Origin mode - RDB
1970 curs.y = (dec_om ? marg_t : 0);
1972 seen_disp_event = TRUE;
1976 case 'm': /* set graphics rendition */
1979 * A VT100 without the AVO only had one attribute, either
1980 * underline or reverse video depending on the cursor type,
1981 * this was selected by CSI 7m.
1984 * This is sometimes DIM, eg on the GIGI and Linux
1986 * This is sometimes INVIS various ANSI.
1988 * This like 22 disables BOLD, DIM and INVIS
1990 * The ANSI colours appear on any terminal that has colour
1991 * (obviously) but the interaction between sgr0 and the
1992 * colours varies but is usually related to the background
1993 * colour erase item.
1994 * The interaction between colour attributes and the mono
1995 * ones is also very implementation dependent.
1997 * The 39 and 49 attributes are likely to be unimplemented.
2000 for (i = 0; i < esc_nargs; i++) {
2001 switch (def(esc_args[i], 0)) {
2002 case 0: /* restore defaults */
2003 curr_attr = ATTR_DEFAULT;
2005 case 1: /* enable bold */
2006 compatibility(VT100AVO);
2007 curr_attr |= ATTR_BOLD;
2009 case 21: /* (enable double underline) */
2010 compatibility(OTHER);
2011 case 4: /* enable underline */
2012 compatibility(VT100AVO);
2013 curr_attr |= ATTR_UNDER;
2015 case 5: /* enable blink */
2016 compatibility(VT100AVO);
2017 curr_attr |= ATTR_BLINK;
2019 case 7: /* enable reverse video */
2020 curr_attr |= ATTR_REVERSE;
2022 case 10: /* SCO acs off */
2023 compatibility(SCOANSI);
2024 if (cfg.no_remote_charset) break;
2026 case 11: /* SCO acs on */
2027 compatibility(SCOANSI);
2028 if (cfg.no_remote_charset) break;
2030 case 12: /* SCO acs on flipped */
2031 compatibility(SCOANSI);
2032 if (cfg.no_remote_charset) break;
2034 case 22: /* disable bold */
2035 compatibility2(OTHER, VT220);
2036 curr_attr &= ~ATTR_BOLD;
2038 case 24: /* disable underline */
2039 compatibility2(OTHER, VT220);
2040 curr_attr &= ~ATTR_UNDER;
2042 case 25: /* disable blink */
2043 compatibility2(OTHER, VT220);
2044 curr_attr &= ~ATTR_BLINK;
2046 case 27: /* disable reverse video */
2047 compatibility2(OTHER, VT220);
2048 curr_attr &= ~ATTR_REVERSE;
2059 curr_attr &= ~ATTR_FGMASK;
2061 (esc_args[i] - 30) << ATTR_FGSHIFT;
2063 case 39: /* default-foreground */
2064 curr_attr &= ~ATTR_FGMASK;
2065 curr_attr |= ATTR_DEFFG;
2076 curr_attr &= ~ATTR_BGMASK;
2078 (esc_args[i] - 40) << ATTR_BGSHIFT;
2080 case 49: /* default-background */
2081 curr_attr &= ~ATTR_BGMASK;
2082 curr_attr |= ATTR_DEFBG;
2087 erase_char = (' ' | ATTR_ASCII |
2089 (ATTR_FGMASK | ATTR_BGMASK)));
2092 case 's': /* save cursor */
2095 case 'u': /* restore cursor */
2097 seen_disp_event = TRUE;
2099 case 't': /* set page size - ie window height */
2101 * VT340/VT420 sequence DECSLPP, DEC only allows values
2102 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2103 * illegal values (eg first arg 1..9) for window changing
2107 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2108 compatibility(VT340TEXT);
2109 if (!cfg.no_remote_resize)
2110 request_resize(cols, def(esc_args[0], 24));
2112 } else if (esc_nargs >= 1 &&
2115 compatibility(OTHER);
2117 switch (esc_args[0]) {
2127 if (esc_nargs >= 3) {
2128 if (!cfg.no_remote_resize)
2129 move_window(def(esc_args[1], 0),
2130 def(esc_args[2], 0));
2134 /* We should resize the window to a given
2135 * size in pixels here, but currently our
2136 * resizing code isn't healthy enough to
2140 set_zorder(TRUE); /* move to top */
2143 set_zorder(FALSE); /* move to bottom */
2149 if (esc_nargs >= 3) {
2150 if (!cfg.no_remote_resize)
2151 request_resize(def(esc_args[2], cfg.width),
2152 def(esc_args[1], cfg.height));
2157 set_zoomed(esc_args[1] ? TRUE : FALSE);
2160 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2164 get_window_pos(&x, &y);
2165 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2166 ldisc_send(buf, len, 0);
2169 get_window_pixels(&x, &y);
2170 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2171 ldisc_send(buf, len, 0);
2174 len = sprintf(buf, "\033[8;%d;%dt",
2176 ldisc_send(buf, len, 0);
2180 * Hmmm. Strictly speaking we
2181 * should return `the size of the
2182 * screen in characters', but
2183 * that's not easy: (a) window
2184 * furniture being what it is it's
2185 * hard to compute, and (b) in
2186 * resize-font mode maximising the
2187 * window wouldn't change the
2188 * number of characters. *shrug*. I
2189 * think we'll ignore it for the
2190 * moment and see if anyone
2191 * complains, and then ask them
2192 * what they would like it to do.
2196 p = get_window_title(TRUE);
2198 ldisc_send("\033]L", 3, 0);
2199 ldisc_send(p, len, 0);
2200 ldisc_send("\033\\", 2, 0);
2203 p = get_window_title(FALSE);
2205 ldisc_send("\033]l", 3, 0);
2206 ldisc_send(p, len, 0);
2207 ldisc_send("\033\\", 2, 0);
2213 compatibility(SCOANSI);
2214 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2217 seen_disp_event = TRUE;
2220 compatibility(SCOANSI);
2221 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2224 seen_disp_event = TRUE;
2226 case ANSI('|', '*'):
2227 /* VT420 sequence DECSNLS
2228 * Set number of lines on screen
2229 * VT420 uses VGA like hardware and can support any size in
2230 * reasonable range (24..49 AIUI) with no default specified.
2232 compatibility(VT420);
2233 if (esc_nargs == 1 && esc_args[0] > 0) {
2234 if (!cfg.no_remote_resize)
2235 request_resize(cols, def(esc_args[0], cfg.height));
2239 case ANSI('|', '$'):
2240 /* VT340/VT420 sequence DECSCPP
2241 * Set number of columns per page
2242 * Docs imply range is only 80 or 132, but I'll allow any.
2244 compatibility(VT340TEXT);
2245 if (esc_nargs <= 1) {
2246 if (!cfg.no_remote_resize)
2247 request_resize(def(esc_args[0], cfg.width), rows);
2251 case 'X': /* write N spaces w/o moving cursor */
2252 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2253 compatibility(ANSIMIN);
2255 int n = def(esc_args[0], 1);
2257 unsigned long *p = cpos;
2258 if (n > cols - curs.x)
2262 check_selection(curs, cursplus);
2265 seen_disp_event = TRUE;
2268 case 'x': /* report terminal characteristics */
2269 compatibility(VT100);
2272 int i = def(esc_args[0], 0);
2273 if (i == 0 || i == 1) {
2274 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2276 ldisc_send(buf, 20, 0);
2280 case 'Z': /* BackTab for xterm */
2281 compatibility(OTHER);
2283 int i = def(esc_args[0], 1);
2284 pos old_curs = curs;
2286 for(;i>0 && curs.x>0; i--) {
2289 } while (curs.x >0 && !tabs[curs.x]);
2292 check_selection(old_curs, curs);
2295 case ANSI('L', '='):
2296 compatibility(OTHER);
2297 use_bce = (esc_args[0] <= 0);
2298 erase_char = ERASE_CHAR;
2300 erase_char = (' ' | ATTR_ASCII |
2302 (ATTR_FGMASK | ATTR_BGMASK)));
2304 case ANSI('E', '='):
2305 compatibility(OTHER);
2306 blink_is_real = (esc_args[0] >= 1);
2308 case ANSI('p', '"'):
2309 /* Allow the host to make this emulator a 'perfect' VT102.
2310 * This first appeared in the VT220, but we do need to get
2311 * back to PuTTY mode so I won't check it.
2313 * The arg in 40..42,50 are a PuTTY extension.
2314 * The 2nd arg, 8bit vs 7bit is not checked.
2316 * Setting VT102 mode should also change the Fkeys to
2317 * generate PF* codes as a real VT102 has no Fkeys.
2318 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2321 * Note ESC c will NOT change this!
2324 switch (esc_args[0]) {
2326 compatibility_level &= ~TM_VTXXX;
2327 compatibility_level |= TM_VT102;
2330 compatibility_level &= ~TM_VTXXX;
2331 compatibility_level |= TM_VT220;
2335 if (esc_args[0] > 60 && esc_args[0] < 70)
2336 compatibility_level |= TM_VTXXX;
2340 compatibility_level &= TM_VTXXX;
2343 compatibility_level = TM_PUTTY;
2346 compatibility_level = TM_SCOANSI;
2350 compatibility_level = TM_PUTTY;
2356 /* Change the response to CSI c */
2357 if (esc_args[0] == 50) {
2360 strcpy(id_string, "\033[?");
2361 for (i = 1; i < esc_nargs; i++) {
2363 strcat(id_string, ";");
2364 sprintf(lbuf, "%d", esc_args[i]);
2365 strcat(id_string, lbuf);
2367 strcat(id_string, "c");
2370 /* Is this a good idea ?
2371 * Well we should do a soft reset at this point ...
2373 if (!has_compat(VT420) && has_compat(VT100)) {
2374 if (!cfg.no_remote_resize) {
2376 request_resize(132, 24);
2378 request_resize(80, 24);
2388 case 'P': /* Linux palette sequence */
2389 termstate = SEEN_OSC_P;
2392 case 'R': /* Linux palette reset */
2395 termstate = TOPLEVEL;
2397 case 'W': /* word-set */
2398 termstate = SEEN_OSC_W;
2411 esc_args[0] = 10 * esc_args[0] + c - '0';
2415 * Grotty hack to support xterm and DECterm title
2416 * sequences concurrently.
2418 if (esc_args[0] == 2) {
2422 /* else fall through */
2424 termstate = OSC_STRING;
2430 * This OSC stuff is EVIL. It takes just one character to get into
2431 * sysline mode and it's not initially obvious how to get out.
2432 * So I've added CR and LF as string aborts.
2433 * This shouldn't effect compatibility as I believe embedded
2434 * control characters are supposed to be interpreted (maybe?)
2435 * and they don't display anything useful anyway.
2439 if (c == '\n' || c == '\r') {
2440 termstate = TOPLEVEL;
2441 } else if (c == 0234 || c == '\007') {
2443 * These characters terminate the string; ST and BEL
2444 * terminate the sequence and trigger instant
2445 * processing of it, whereas ESC goes back to SEEN_ESC
2446 * mode unless it is followed by \, in which case it is
2447 * synonymous with ST in the first place.
2450 termstate = TOPLEVEL;
2451 } else if (c == '\033')
2452 termstate = OSC_MAYBE_ST;
2453 else if (osc_strlen < OSC_STR_MAX)
2454 osc_string[osc_strlen++] = c;
2458 int max = (osc_strlen == 0 ? 21 : 16);
2460 if (c >= '0' && c <= '9')
2462 else if (c >= 'A' && c <= 'A' + max - 10)
2464 else if (c >= 'a' && c <= 'a' + max - 10)
2467 termstate = TOPLEVEL;
2470 osc_string[osc_strlen++] = val;
2471 if (osc_strlen >= 7) {
2472 palette_set(osc_string[0],
2473 osc_string[1] * 16 + osc_string[2],
2474 osc_string[3] * 16 + osc_string[4],
2475 osc_string[5] * 16 + osc_string[6]);
2477 termstate = TOPLEVEL;
2493 esc_args[0] = 10 * esc_args[0] + c - '0';
2496 termstate = OSC_STRING;
2501 termstate = TOPLEVEL;
2502 seen_disp_event = TRUE;
2505 move(curs.x, curs.y - 1, 1);
2508 move(curs.x, curs.y + 1, 1);
2511 move(curs.x + 1, curs.y, 1);
2514 move(curs.x - 1, curs.y, 1);
2517 * From the VT100 Manual
2518 * NOTE: The special graphics characters in the VT100
2519 * are different from those in the VT52
2521 * From VT102 manual:
2522 * 137 _ Blank - Same
2523 * 140 ` Reserved - Humm.
2524 * 141 a Solid rectangle - Similar
2525 * 142 b 1/ - Top half of fraction for the
2526 * 143 c 3/ - subscript numbers below.
2529 * 146 f Degrees - Same
2530 * 147 g Plus or minus - Same
2532 * 151 i Ellipsis (dots)
2535 * 154 l Bar at scan 0
2536 * 155 m Bar at scan 1
2537 * 156 n Bar at scan 2
2538 * 157 o Bar at scan 3 - Similar
2539 * 160 p Bar at scan 4 - Similar
2540 * 161 q Bar at scan 5 - Similar
2541 * 162 r Bar at scan 6 - Same
2542 * 163 s Bar at scan 7 - Similar
2557 cset_attr[cset = 0] = ATTR_LINEDRW;
2560 cset_attr[cset = 0] = ATTR_ASCII;
2567 scroll(0, rows - 1, -1, TRUE);
2568 else if (curs.y > 0)
2574 erase_lots(FALSE, FALSE, TRUE);
2578 erase_lots(TRUE, FALSE, TRUE);
2582 /* XXX Print cursor line */
2585 /* XXX Start controller mode */
2588 /* XXX Stop controller mode */
2592 termstate = VT52_Y1;
2595 ldisc_send("\033/Z", 3, 0);
2598 app_keypad_keys = TRUE;
2601 app_keypad_keys = FALSE;
2604 /* XXX This should switch to VT100 mode not current or default
2605 * VT mode. But this will only have effect in a VT220+
2609 blink_is_real = cfg.blinktext;
2613 /* XXX Enter auto print mode */
2616 /* XXX Exit auto print mode */
2619 /* XXX Print screen */
2625 /* compatibility(ATARI) */
2627 erase_lots(FALSE, FALSE, TRUE);
2631 /* compatibility(ATARI) */
2632 if (curs.y <= marg_b)
2633 scroll(curs.y, marg_b, -1, FALSE);
2636 /* compatibility(ATARI) */
2637 if (curs.y <= marg_b)
2638 scroll(curs.y, marg_b, 1, TRUE);
2641 /* compatibility(ATARI) */
2642 termstate = VT52_FG;
2645 /* compatibility(ATARI) */
2646 termstate = VT52_BG;
2649 /* compatibility(ATARI) */
2650 erase_lots(FALSE, TRUE, FALSE);
2654 /* compatibility(ATARI) */
2658 /* compatibility(ATARI) */
2661 /* case 'j': Save cursor position - broken on ST */
2662 /* case 'k': Restore cursor position */
2664 /* compatibility(ATARI) */
2665 erase_lots(TRUE, TRUE, TRUE);
2671 /* compatibility(ATARI) */
2672 erase_lots(TRUE, TRUE, FALSE);
2675 /* compatibility(ATARI) */
2676 curr_attr |= ATTR_REVERSE;
2679 /* compatibility(ATARI) */
2680 curr_attr &= ~ATTR_REVERSE;
2682 case 'v': /* wrap Autowrap on - Wyse style */
2683 /* compatibility(ATARI) */
2686 case 'w': /* Autowrap off */
2687 /* compatibility(ATARI) */
2692 /* compatibility(OTHER) */
2694 curr_attr = ATTR_DEFAULT;
2696 erase_char = (' ' | ATTR_ASCII |
2698 (ATTR_FGMASK | ATTR_BGMASK)));
2701 /* compatibility(VI50) */
2702 curr_attr |= ATTR_UNDER;
2705 /* compatibility(VI50) */
2706 curr_attr &= ~ATTR_UNDER;
2709 /* compatibility(VI50) */
2711 curr_attr |= ATTR_BOLD;
2714 /* compatibility(VI50) */
2716 curr_attr &= ~ATTR_BOLD;
2722 termstate = VT52_Y2;
2723 move(curs.x, c - ' ', 0);
2726 termstate = TOPLEVEL;
2727 move(c - ' ', curs.y, 0);
2732 termstate = TOPLEVEL;
2733 curr_attr &= ~ATTR_FGMASK;
2734 curr_attr &= ~ATTR_BOLD;
2735 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2736 if ((c & 0x8) || vt52_bold)
2737 curr_attr |= ATTR_BOLD;
2740 erase_char = (' ' | ATTR_ASCII |
2741 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2744 termstate = TOPLEVEL;
2745 curr_attr &= ~ATTR_BGMASK;
2746 curr_attr &= ~ATTR_BLINK;
2747 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2749 /* Note: bold background */
2751 curr_attr |= ATTR_BLINK;
2754 erase_char = (' ' | ATTR_ASCII |
2755 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2758 default: break; /* placate gcc warning about enum use */
2760 if (selstate != NO_SELECTION) {
2761 pos cursplus = curs;
2763 check_selection(curs, cursplus);
2772 * Compare two lines to determine whether they are sufficiently
2773 * alike to scroll-optimise one to the other. Return the degree of
2776 static int linecmp(unsigned long *a, unsigned long *b)
2780 for (i = n = 0; i < cols; i++)
2781 n += (*a++ == *b++);
2787 * Given a context, update the window. Out of paranoia, we don't
2788 * allow WM_PAINT responses to do scrolling optimisations.
2790 static void do_paint(Context ctx, int may_optimise)
2792 int i, j, our_curs_y;
2793 unsigned long rv, cursor;
2796 long cursor_background = ERASE_CHAR;
2797 unsigned long ticks;
2800 * Check the visual bell state.
2803 ticks = GETTICKCOUNT();
2804 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2808 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2811 * screen array, disptop, scrtop,
2813 * cfg.blinkpc, blink_is_real, tblinker,
2814 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2817 /* Has the cursor position or type changed ? */
2820 if (blinker || !cfg.blink_cur)
2821 cursor = TATTR_ACTCURS;
2825 cursor = TATTR_PASCURS;
2827 cursor |= TATTR_RIGHTCURS;
2830 our_curs_y = curs.y - disptop;
2832 if (dispcurs && (curstype != cursor ||
2834 disptext + our_curs_y * (cols + 1) + curs.x)) {
2835 if (dispcurs > disptext &&
2836 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2837 dispcurs[-1] |= ATTR_INVALID;
2838 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2839 dispcurs[1] |= ATTR_INVALID;
2840 *dispcurs |= ATTR_INVALID;
2845 /* The normal screen data */
2846 for (i = 0; i < rows; i++) {
2847 unsigned long *ldata;
2849 int idx, dirty_line, dirty_run, selected;
2850 unsigned long attr = 0;
2851 int updated_line = 0;
2854 int last_run_dirty = 0;
2856 scrpos.y = i + disptop;
2857 ldata = lineptr(scrpos.y);
2858 lattr = (ldata[cols] & LATTR_MODE);
2860 idx = i * (cols + 1);
2861 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2862 disptext[idx + cols] = ldata[cols];
2864 for (j = 0; j < cols; j++, idx++) {
2865 unsigned long tattr, tchar;
2866 unsigned long *d = ldata + j;
2870 tchar = (*d & (CHAR_MASK | CSET_MASK));
2871 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2872 switch (tchar & CSET_MASK) {
2874 tchar = unitab_line[tchar & 0xFF];
2877 tchar = unitab_xterm[tchar & 0xFF];
2880 tchar = unitab_scoacs[tchar&0xFF];
2883 tattr |= (tchar & CSET_MASK);
2885 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2888 /* Video reversing things */
2889 if (seltype == LEXICOGRAPHIC)
2890 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2892 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2894 ^ (selected ? ATTR_REVERSE : 0));
2896 /* 'Real' blinking ? */
2897 if (blink_is_real && (tattr & ATTR_BLINK)) {
2898 if (has_focus && tblinker) {
2900 tattr &= ~CSET_MASK;
2903 tattr &= ~ATTR_BLINK;
2907 * Check the font we'll _probably_ be using to see if
2908 * the character is wide when we don't want it to be.
2910 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2911 if ((tattr & ATTR_WIDE) == 0 &&
2912 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2913 tattr |= ATTR_NARROW;
2914 } else if (disptext[idx]&ATTR_NARROW)
2915 tattr |= ATTR_NARROW;
2917 /* Cursor here ? Save the 'background' */
2918 if (i == our_curs_y && j == curs.x) {
2919 cursor_background = tattr | tchar;
2920 dispcurs = disptext + idx;
2923 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2926 break_run = (tattr != attr || j - start >= sizeof(ch));
2928 /* Special hack for VT100 Linedraw glyphs */
2929 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2930 && tchar <= 0xBD) break_run = TRUE;
2932 if (!dbcs_screenfont && !dirty_line) {
2933 if ((tchar | tattr) == disptext[idx])
2935 else if (!dirty_run && ccount == 1)
2940 if ((dirty_run || last_run_dirty) && ccount > 0) {
2941 do_text(ctx, start, i, ch, ccount, attr, lattr);
2947 if (dbcs_screenfont)
2948 last_run_dirty = dirty_run;
2949 dirty_run = dirty_line;
2952 if ((tchar | tattr) != disptext[idx])
2954 ch[ccount++] = (char) tchar;
2955 disptext[idx] = tchar | tattr;
2957 /* If it's a wide char step along to the next one. */
2958 if (tattr & ATTR_WIDE) {
2962 /* Cursor is here ? Ouch! */
2963 if (i == our_curs_y && j == curs.x) {
2964 cursor_background = *d;
2965 dispcurs = disptext + idx;
2967 if (disptext[idx] != *d)
2973 if (dirty_run && ccount > 0) {
2974 do_text(ctx, start, i, ch, ccount, attr, lattr);
2978 /* Cursor on this line ? (and changed) */
2979 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2980 ch[0] = (char) (cursor_background & CHAR_MASK);
2981 attr = (cursor_background & ATTR_MASK) | cursor;
2982 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2989 * Flick the switch that says if blinking things should be shown or hidden.
2992 void term_blink(int flg)
2994 static long last_blink = 0;
2995 static long last_tblink = 0;
2996 long now, blink_diff;
2998 now = GETTICKCOUNT();
2999 blink_diff = now - last_tblink;
3001 /* Make sure the text blinks no more than 2Hz */
3002 if (blink_diff < 0 || blink_diff > 450) {
3004 tblinker = !tblinker;
3013 blink_diff = now - last_blink;
3015 /* Make sure the cursor blinks no faster than system blink rate */
3016 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3024 * Invalidate the whole screen so it will be repainted in full.
3026 void term_invalidate(void)
3030 for (i = 0; i < rows * (cols + 1); i++)
3031 disptext[i] = ATTR_INVALID;
3035 * Paint the window in response to a WM_PAINT message.
3037 void term_paint(Context ctx, int left, int top, int right, int bottom)
3040 if (left < 0) left = 0;
3041 if (top < 0) top = 0;
3042 if (right >= cols) right = cols-1;
3043 if (bottom >= rows) bottom = rows-1;
3045 for (i = top; i <= bottom && i < rows; i++) {
3046 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
3047 for (j = left; j <= right && j < cols; j++)
3048 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3050 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
3051 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3054 /* This should happen soon enough, also for some reason it sometimes
3055 * fails to actually do anything when re-sizing ... painting the wrong
3059 do_paint (ctx, FALSE);
3063 * Attempt to scroll the scrollback. The second parameter gives the
3064 * position we want to scroll to; the first is +1 to denote that
3065 * this position is relative to the beginning of the scrollback, -1
3066 * to denote it is relative to the end, and 0 to denote that it is
3067 * relative to the current position.
3069 void term_scroll(int rel, int where)
3071 int sbtop = -count234(scrollback);
3073 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
3074 if (disptop < sbtop)
3082 static void clipme(pos top, pos bottom, int rect)
3085 wchar_t *wbptr; /* where next char goes within workbuf */
3087 int wblen = 0; /* workbuf len */
3088 int buflen; /* amount of memory allocated to workbuf */
3090 buflen = 5120; /* Default size */
3091 workbuf = smalloc(buflen * sizeof(wchar_t));
3092 wbptr = workbuf; /* start filling here */
3093 old_top_x = top.x; /* needed for rect==1 */
3095 while (poslt(top, bottom)) {
3097 unsigned long *ldata = lineptr(top.y);
3101 * nlpos will point at the maximum position on this line we
3102 * should copy up to. So we start it at the end of the
3109 * ... move it backwards if there's unused space at the end
3110 * of the line (and also set `nl' if this is the case,
3111 * because in normal selection mode this means we need a
3112 * newline at the end)...
3114 if (!(ldata[cols] & LATTR_WRAPPED)) {
3115 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3116 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3117 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3118 && poslt(top, nlpos))
3120 if (poslt(nlpos, bottom))
3125 * ... and then clip it to the terminal x coordinate if
3126 * we're doing rectangular selection. (In this case we
3127 * still did the above, so that copying e.g. the right-hand
3128 * column from a table doesn't fill with spaces on the
3132 if (nlpos.x > bottom.x)
3134 nl = (top.y < bottom.y);
3137 while (poslt(top, bottom) && poslt(top, nlpos)) {
3140 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3142 wchar_t cbuf[16], *p;
3143 int uc = (ldata[top.x] & 0xFFFF);
3146 if (uc == UCSWIDE) {
3151 switch (uc & CSET_MASK) {
3154 uc = unitab_xterm[uc & 0xFF];
3158 uc = unitab_line[uc & 0xFF];
3161 uc = unitab_scoacs[uc&0xFF];
3164 switch (uc & CSET_MASK) {
3166 uc = unitab_font[uc & 0xFF];
3169 uc = unitab_oemcp[uc & 0xFF];
3173 set = (uc & CSET_MASK);
3174 c = (uc & CHAR_MASK);
3178 if (DIRECT_FONT(uc)) {
3179 if (c >= ' ' && c != 0x7F) {
3180 unsigned char buf[4];
3183 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3185 buf[1] = (unsigned char) ldata[top.x + 1];
3186 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3190 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3194 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3201 for (p = cbuf; *p; p++) {
3202 /* Enough overhead for trailing NL and nul */
3203 if (wblen >= buflen - 16) {
3206 sizeof(wchar_t) * (buflen += 100));
3207 wbptr = workbuf + wblen;
3216 for (i = 0; i < sel_nl_sz; i++) {
3218 *wbptr++ = sel_nl[i];
3222 top.x = rect ? old_top_x : 0;
3224 #if SELECTION_NUL_TERMINATED
3228 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3229 if (buflen > 0) /* indicates we allocated this buffer */
3233 void term_copyall(void)
3236 top.y = -count234(scrollback);
3238 clipme(top, curs, 0);
3242 * The wordness array is mainly for deciding the disposition of the US-ASCII
3245 static int wordtype(int uc)
3248 int start, end, ctype;
3249 } *wptr, ucs_words[] = {
3255 0x037e, 0x037e, 1}, /* Greek question mark */
3257 0x0387, 0x0387, 1}, /* Greek ano teleia */
3259 0x055a, 0x055f, 1}, /* Armenian punctuation */
3261 0x0589, 0x0589, 1}, /* Armenian full stop */
3263 0x0700, 0x070d, 1}, /* Syriac punctuation */
3265 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3267 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3269 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3271 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3273 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3275 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3277 0x2000, 0x200a, 0}, /* Various spaces */
3279 0x2070, 0x207f, 2}, /* superscript */
3281 0x2080, 0x208f, 2}, /* subscript */
3283 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3285 0x3000, 0x3000, 0}, /* ideographic space */
3287 0x3001, 0x3020, 1}, /* ideographic punctuation */
3289 0x303f, 0x309f, 3}, /* Hiragana */
3291 0x30a0, 0x30ff, 3}, /* Katakana */
3293 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3295 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3297 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3299 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3301 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3303 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3305 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3307 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3309 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3314 uc &= (CSET_MASK | CHAR_MASK);
3316 switch (uc & CSET_MASK) {
3318 uc = unitab_xterm[uc & 0xFF];
3321 uc = unitab_line[uc & 0xFF];
3324 uc = unitab_scoacs[uc&0xFF];
3327 switch (uc & CSET_MASK) {
3329 uc = unitab_font[uc & 0xFF];
3332 uc = unitab_oemcp[uc & 0xFF];
3336 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3337 * fail as there's such a thing as a double width space. :-(
3339 if (dbcs_screenfont && font_codepage == line_codepage)
3343 return wordness[uc];
3345 for (wptr = ucs_words; wptr->start; wptr++) {
3346 if (uc >= wptr->start && uc <= wptr->end)
3354 * Spread the selection outwards according to the selection mode.
3356 static pos sel_spread_half(pos p, int dir)
3358 unsigned long *ldata;
3360 int topy = -count234(scrollback);
3362 ldata = lineptr(p.y);
3367 * In this mode, every character is a separate unit, except
3368 * for runs of spaces at the end of a non-wrapping line.
3370 if (!(ldata[cols] & LATTR_WRAPPED)) {
3371 unsigned long *q = ldata + cols;
3372 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3374 if (q == ldata + cols)
3376 if (p.x >= q - ldata)
3377 p.x = (dir == -1 ? q - ldata : cols - 1);
3382 * In this mode, the units are maximal runs of characters
3383 * whose `wordness' has the same value.
3385 wvalue = wordtype(ldata[p.x]);
3389 if (wordtype(ldata[p.x + 1]) == wvalue)
3394 if (ldata[cols] & LATTR_WRAPPED) {
3395 unsigned long *ldata2;
3396 ldata2 = lineptr(p.y+1);
3397 if (wordtype(ldata2[0]) == wvalue) {
3410 if (wordtype(ldata[p.x - 1]) == wvalue)
3415 unsigned long *ldata2;
3418 ldata2 = lineptr(p.y-1);
3419 if ((ldata2[cols] & LATTR_WRAPPED) &&
3420 wordtype(ldata2[cols-1]) == wvalue) {
3432 * In this mode, every line is a unit.
3434 p.x = (dir == -1 ? 0 : cols - 1);
3440 static void sel_spread(void)
3442 if (seltype == LEXICOGRAPHIC) {
3443 selstart = sel_spread_half(selstart, -1);
3445 selend = sel_spread_half(selend, +1);
3450 void term_do_paste(void)
3455 get_clip(&data, &len);
3460 sfree(paste_buffer);
3461 paste_pos = paste_hold = paste_len = 0;
3462 paste_buffer = smalloc(len * sizeof(wchar_t));
3465 while (p < data + len) {
3466 while (p < data + len &&
3467 !(p <= data + len - sel_nl_sz &&
3468 !memcmp(p, sel_nl, sizeof(sel_nl))))
3473 for (i = 0; i < p - q; i++) {
3474 paste_buffer[paste_len++] = q[i];
3478 if (p <= data + len - sel_nl_sz &&
3479 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3480 paste_buffer[paste_len++] = '\r';
3486 /* Assume a small paste will be OK in one go. */
3487 if (paste_len < 256) {
3488 luni_send(paste_buffer, paste_len, 0);
3490 sfree(paste_buffer);
3492 paste_pos = paste_hold = paste_len = 0;
3495 get_clip(NULL, NULL);
3498 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3499 int shift, int ctrl, int alt)
3502 unsigned long *ldata;
3503 int raw_mouse = (xterm_mouse &&
3504 !cfg.no_mouse_rep &&
3505 !(cfg.mouse_override && shift));
3506 int default_seltype;
3510 if (a == MA_DRAG && !raw_mouse)
3515 if (a == MA_DRAG && !raw_mouse)
3528 selpoint.y = y + disptop;
3530 ldata = lineptr(selpoint.y);
3531 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3535 int encstate = 0, r, c;
3537 static int is_down = 0;
3541 encstate = 0x20; /* left button down */
3552 case MBT_WHEEL_DOWN:
3555 default: break; /* placate gcc warning about enum use */
3559 if (xterm_mouse == 1)
3572 default: break; /* placate gcc warning about enum use */
3581 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3582 ldisc_send(abuf, 6, 0);
3586 b = translate_button(b);
3589 * Set the selection type (rectangular or normal) at the start
3590 * of a selection attempt, from the state of Alt.
3592 if (!alt ^ !cfg.rect_select)
3593 default_seltype = RECTANGULAR;
3595 default_seltype = LEXICOGRAPHIC;
3597 if (selstate == NO_SELECTION) {
3598 seltype = default_seltype;
3601 if (b == MBT_SELECT && a == MA_CLICK) {
3603 selstate = ABOUT_TO;
3604 seltype = default_seltype;
3605 selanchor = selpoint;
3607 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3609 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3610 selstate = DRAGGING;
3611 selstart = selanchor = selpoint;
3615 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3616 (b == MBT_EXTEND && a != MA_RELEASE)) {
3617 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3619 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3620 if (seltype == LEXICOGRAPHIC) {
3622 * For normal selection, we extend by moving
3623 * whichever end of the current selection is closer
3626 if (posdiff(selpoint, selstart) <
3627 posdiff(selend, selstart) / 2) {
3631 selanchor = selstart;
3635 * For rectangular selection, we have a choice of
3636 * _four_ places to put selanchor and selpoint: the
3637 * four corners of the selection.
3639 if (2*selpoint.x < selstart.x + selend.x)
3640 selanchor.x = selend.x-1;
3642 selanchor.x = selstart.x;
3644 if (2*selpoint.y < selstart.y + selend.y)
3645 selanchor.y = selend.y;
3647 selanchor.y = selstart.y;
3649 selstate = DRAGGING;
3651 if (selstate != ABOUT_TO && selstate != DRAGGING)
3652 selanchor = selpoint;
3653 selstate = DRAGGING;
3654 if (seltype == LEXICOGRAPHIC) {
3656 * For normal selection, we set (selstart,selend) to
3657 * (selpoint,selanchor) in some order.
3659 if (poslt(selpoint, selanchor)) {
3660 selstart = selpoint;
3664 selstart = selanchor;
3670 * For rectangular selection, we may need to
3671 * interchange x and y coordinates (if the user has
3672 * dragged in the -x and +y directions, or vice versa).
3674 selstart.x = min(selanchor.x, selpoint.x);
3675 selend.x = 1+max(selanchor.x, selpoint.x);
3676 selstart.y = min(selanchor.y, selpoint.y);
3677 selend.y = max(selanchor.y, selpoint.y);
3680 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3681 if (selstate == DRAGGING) {
3683 * We've completed a selection. We now transfer the
3684 * data to the clipboard.
3686 clipme(selstart, selend, (seltype == RECTANGULAR));
3687 selstate = SELECTED;
3689 selstate = NO_SELECTION;
3690 } else if (b == MBT_PASTE
3692 #if MULTICLICK_ONLY_EVENT
3693 || a == MA_2CLK || a == MA_3CLK
3706 sfree(paste_buffer);
3713 static long last_paste = 0;
3714 long now, paste_diff;
3719 /* Don't wait forever to paste */
3721 now = GETTICKCOUNT();
3722 paste_diff = now - last_paste;
3723 if (paste_diff >= 0 && paste_diff < 450)
3728 while (paste_pos < paste_len) {
3730 while (n + paste_pos < paste_len) {
3731 if (paste_buffer[paste_pos + n++] == '\r')
3734 luni_send(paste_buffer + paste_pos, n, 0);
3737 if (paste_pos < paste_len) {
3742 sfree(paste_buffer);
3747 static void deselect(void)
3749 selstate = NO_SELECTION;
3750 selstart.x = selstart.y = selend.x = selend.y = 0;
3753 void term_deselect(void)
3759 int term_ldisc(int option)
3761 if (option == LD_ECHO)
3762 return term_echoing;
3763 if (option == LD_EDIT)
3764 return term_editing;
3769 * from_backend(), to get data from the backend for the terminal.
3771 int from_backend(int is_stderr, char *data, int len)
3775 bufchain_add(&inbuf, data, len);
3778 * term_out() always completely empties inbuf. Therefore,
3779 * there's no reason at all to return anything other than zero
3780 * from this function, because there _can't_ be a question of
3781 * the remote side needing to wait until term_out() has cleared
3784 * This is a slightly suboptimal way to deal with SSH2 - in
3785 * principle, the window mechanism would allow us to continue
3786 * to accept data on forwarded ports and X connections even
3787 * while the terminal processing was going slowly - but we
3788 * can't do the 100% right thing without moving the terminal
3789 * processing into a separate thread, and that might hurt
3790 * portability. So we manage stdout buffering the old SSH1 way:
3791 * if the terminal processing goes slowly, the whole SSH
3792 * connection stops accepting data until it's ready.
3794 * In practice, I can't imagine this causing serious trouble.