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 term_print_finish();
1142 /* First see about all those translations. */
1143 if (termstate == TOPLEVEL) {
1145 switch (utf_state) {
1148 /* UTF-8 must be stateless so we ignore iso2022. */
1149 if (unitab_ctrl[c] != 0xFF)
1151 else c = ((unsigned char)c) | ATTR_ASCII;
1153 } else if ((c & 0xe0) == 0xc0) {
1154 utf_size = utf_state = 1;
1155 utf_char = (c & 0x1f);
1156 } else if ((c & 0xf0) == 0xe0) {
1157 utf_size = utf_state = 2;
1158 utf_char = (c & 0x0f);
1159 } else if ((c & 0xf8) == 0xf0) {
1160 utf_size = utf_state = 3;
1161 utf_char = (c & 0x07);
1162 } else if ((c & 0xfc) == 0xf8) {
1163 utf_size = utf_state = 4;
1164 utf_char = (c & 0x03);
1165 } else if ((c & 0xfe) == 0xfc) {
1166 utf_size = utf_state = 5;
1167 utf_char = (c & 0x01);
1178 if ((c & 0xC0) != 0x80) {
1184 utf_char = (utf_char << 6) | (c & 0x3f);
1190 /* Is somebody trying to be evil! */
1192 (c < 0x800 && utf_size >= 2) ||
1193 (c < 0x10000 && utf_size >= 3) ||
1194 (c < 0x200000 && utf_size >= 4) ||
1195 (c < 0x4000000 && utf_size >= 5))
1198 /* Unicode line separator and paragraph separator are CR-LF */
1199 if (c == 0x2028 || c == 0x2029)
1202 /* High controls are probably a Baaad idea too. */
1206 /* The UTF-16 surrogates are not nice either. */
1207 /* The standard give the option of decoding these:
1208 * I don't want to! */
1209 if (c >= 0xD800 && c < 0xE000)
1212 /* ISO 10646 characters now limited to UTF-16 range. */
1216 /* This is currently a TagPhobic application.. */
1217 if (c >= 0xE0000 && c <= 0xE007F)
1220 /* U+FEFF is best seen as a null. */
1223 /* But U+FFFE is an error. */
1224 if (c == 0xFFFE || c == 0xFFFF)
1227 /* Oops this is a 16bit implementation */
1232 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1234 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1236 if (sco_acs == 2) c ^= 0x80;
1239 switch (cset_attr[cset]) {
1241 * Linedraw characters are different from 'ESC ( B'
1242 * only for a small range. For ones outside that
1243 * range, make sure we use the same font as well as
1244 * the same encoding.
1247 if (unitab_ctrl[c] != 0xFF)
1250 c = ((unsigned char) c) | ATTR_LINEDRW;
1254 /* If UK-ASCII, make the '#' a LineDraw Pound */
1256 c = '}' | ATTR_LINEDRW;
1259 /*FALLTHROUGH*/ case ATTR_ASCII:
1260 if (unitab_ctrl[c] != 0xFF)
1263 c = ((unsigned char) c) | ATTR_ASCII;
1266 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1272 /* How about C1 controls ? */
1273 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1274 has_compat(VT220)) {
1275 termstate = SEEN_ESC;
1277 c = '@' + (c & 0x1F);
1280 /* Or the GL control. */
1281 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1282 if (curs.x && !wrapnext)
1286 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1287 *cpos = (' ' | curr_attr | ATTR_ASCII);
1289 /* Or normal C0 controls. */
1290 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1292 case '\005': /* terminal type query */
1293 /* Strictly speaking this is VT100 but a VT100 defaults to
1294 * no response. Other terminals respond at their option.
1296 * Don't put a CR in the default string as this tends to
1297 * upset some weird software.
1299 * An xterm returns "xterm" (5 characters)
1301 compatibility(ANSIMIN);
1303 char abuf[256], *s, *d;
1305 for (s = cfg.answerback, d = abuf; *s; s++) {
1307 if (*s >= 'a' && *s <= 'z')
1308 *d++ = (*s - ('a' - 1));
1309 else if ((*s >= '@' && *s <= '_') ||
1310 *s == '?' || (*s & 0x80))
1315 } else if (*s == '^') {
1320 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1325 struct beeptime *newbeep;
1326 unsigned long ticks;
1328 ticks = GETTICKCOUNT();
1330 if (!beep_overloaded) {
1331 newbeep = smalloc(sizeof(struct beeptime));
1332 newbeep->ticks = ticks;
1333 newbeep->next = NULL;
1337 beeptail->next = newbeep;
1343 * Throw out any beeps that happened more than
1347 beephead->ticks < ticks - cfg.bellovl_t) {
1348 struct beeptime *tmp = beephead;
1349 beephead = tmp->next;
1356 if (cfg.bellovl && beep_overloaded &&
1357 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1359 * If we're currently overloaded and the
1360 * last beep was more than s seconds ago,
1361 * leave overload mode.
1363 beep_overloaded = FALSE;
1364 } else if (cfg.bellovl && !beep_overloaded &&
1365 nbeeps >= cfg.bellovl_n) {
1367 * Now, if we have n or more beeps
1368 * remaining in the queue, go into overload
1371 beep_overloaded = TRUE;
1376 * Perform an actual beep if we're not overloaded.
1378 if (!cfg.bellovl || !beep_overloaded) {
1379 if (cfg.beep == BELL_VISUAL) {
1381 vbell_startpoint = ticks;
1390 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1391 else if (curs.x == 0 && curs.y > 0)
1392 curs.x = cols - 1, curs.y--;
1398 seen_disp_event = TRUE;
1401 compatibility(VT100);
1405 compatibility(VT100);
1410 termstate = VT52_ESC;
1412 compatibility(ANSIMIN);
1413 termstate = SEEN_ESC;
1421 seen_disp_event = TRUE;
1423 logtraffic((unsigned char) c, LGTYP_ASCII);
1426 if (has_compat(SCOANSI)) {
1428 erase_lots(FALSE, FALSE, TRUE);
1431 seen_disp_event = 1;
1435 compatibility(VT100);
1437 if (curs.y == marg_b)
1438 scroll(marg_t, marg_b, 1, TRUE);
1439 else if (curs.y < rows - 1)
1445 seen_disp_event = 1;
1447 logtraffic((unsigned char) c, LGTYP_ASCII);
1451 pos old_curs = curs;
1452 unsigned long *ldata = lineptr(curs.y);
1456 } while (curs.x < cols - 1 && !tabs[curs.x]);
1458 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1459 if (curs.x >= cols / 2)
1460 curs.x = cols / 2 - 1;
1467 check_selection(old_curs, curs);
1469 seen_disp_event = TRUE;
1473 switch (termstate) {
1475 /* Only graphic characters get this far, ctrls are stripped above */
1476 if (wrapnext && wrap) {
1477 cpos[1] |= LATTR_WRAPPED;
1478 if (curs.y == marg_b)
1479 scroll(marg_t, marg_b, 1, TRUE);
1480 else if (curs.y < rows - 1)
1488 if (selstate != NO_SELECTION) {
1489 pos cursplus = curs;
1491 check_selection(curs, cursplus);
1493 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1494 logtraffic((unsigned char) c, LGTYP_ASCII);
1496 extern int wcwidth(wchar_t ucs);
1501 width = wcwidth((wchar_t) c);
1504 *cpos++ = c | curr_attr;
1505 if (++curs.x == cols) {
1506 *cpos |= LATTR_WRAPPED;
1507 if (curs.y == marg_b)
1508 scroll(marg_t, marg_b, 1, TRUE);
1509 else if (curs.y < rows - 1)
1514 *cpos++ = UCSWIDE | curr_attr;
1517 *cpos++ = c | curr_attr;
1524 if (curs.x == cols) {
1528 if (wrap && vt52_mode) {
1529 cpos[1] |= LATTR_WRAPPED;
1530 if (curs.y == marg_b)
1531 scroll(marg_t, marg_b, 1, TRUE);
1532 else if (curs.y < rows - 1)
1539 seen_disp_event = 1;
1544 * This state is virtually identical to SEEN_ESC, with the
1545 * exception that we have an OSC sequence in the pipeline,
1546 * and _if_ we see a backslash, we process it.
1550 termstate = TOPLEVEL;
1553 /* else fall through */
1555 if (c >= ' ' && c <= '/') {
1562 termstate = TOPLEVEL;
1563 switch (ANSI(c, esc_query)) {
1564 case '[': /* enter CSI mode */
1565 termstate = SEEN_CSI;
1567 esc_args[0] = ARG_DEFAULT;
1570 case ']': /* xterm escape sequences */
1571 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1572 compatibility(OTHER);
1573 termstate = SEEN_OSC;
1576 case '7': /* save cursor */
1577 compatibility(VT100);
1580 case '8': /* restore cursor */
1581 compatibility(VT100);
1583 seen_disp_event = TRUE;
1586 compatibility(VT100);
1587 app_keypad_keys = TRUE;
1590 compatibility(VT100);
1591 app_keypad_keys = FALSE;
1593 case 'D': /* exactly equivalent to LF */
1594 compatibility(VT100);
1595 if (curs.y == marg_b)
1596 scroll(marg_t, marg_b, 1, TRUE);
1597 else if (curs.y < rows - 1)
1601 seen_disp_event = TRUE;
1603 case 'E': /* exactly equivalent to CR-LF */
1604 compatibility(VT100);
1606 if (curs.y == marg_b)
1607 scroll(marg_t, marg_b, 1, TRUE);
1608 else if (curs.y < rows - 1)
1612 seen_disp_event = TRUE;
1614 case 'M': /* reverse index - backwards LF */
1615 compatibility(VT100);
1616 if (curs.y == marg_t)
1617 scroll(marg_t, marg_b, -1, TRUE);
1618 else if (curs.y > 0)
1622 seen_disp_event = TRUE;
1624 case 'Z': /* terminal type query */
1625 compatibility(VT100);
1626 ldisc_send(id_string, strlen(id_string), 0);
1628 case 'c': /* restore power-on settings */
1629 compatibility(VT100);
1632 if (!cfg.no_remote_resize)
1633 request_resize(80, rows);
1638 seen_disp_event = TRUE;
1640 case 'H': /* set a tab */
1641 compatibility(VT100);
1642 tabs[curs.x] = TRUE;
1645 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1646 compatibility(VT100);
1648 unsigned long *ldata;
1652 for (i = 0; i < rows; i++) {
1654 for (j = 0; j < cols; j++)
1655 ldata[j] = ATTR_DEFAULT | 'E';
1659 seen_disp_event = TRUE;
1660 scrtop.x = scrtop.y = 0;
1663 check_selection(scrtop, scrbot);
1667 case ANSI('3', '#'):
1668 case ANSI('4', '#'):
1669 case ANSI('5', '#'):
1670 case ANSI('6', '#'):
1671 compatibility(VT100);
1673 unsigned long nlattr;
1674 unsigned long *ldata;
1675 switch (ANSI(c, esc_query)) {
1676 case ANSI('3', '#'):
1679 case ANSI('4', '#'):
1682 case ANSI('5', '#'):
1683 nlattr = LATTR_NORM;
1685 default: /* spiritually case ANSI('6', '#'): */
1686 nlattr = LATTR_WIDE;
1689 ldata = lineptr(curs.y);
1690 ldata[cols] &= ~LATTR_MODE;
1691 ldata[cols] |= nlattr;
1695 case ANSI('A', '('):
1696 compatibility(VT100);
1697 if (!cfg.no_remote_charset)
1698 cset_attr[0] = ATTR_GBCHR;
1700 case ANSI('B', '('):
1701 compatibility(VT100);
1702 if (!cfg.no_remote_charset)
1703 cset_attr[0] = ATTR_ASCII;
1705 case ANSI('0', '('):
1706 compatibility(VT100);
1707 if (!cfg.no_remote_charset)
1708 cset_attr[0] = ATTR_LINEDRW;
1710 case ANSI('U', '('):
1711 compatibility(OTHER);
1712 if (!cfg.no_remote_charset)
1713 cset_attr[0] = ATTR_SCOACS;
1716 case ANSI('A', ')'):
1717 compatibility(VT100);
1718 if (!cfg.no_remote_charset)
1719 cset_attr[1] = ATTR_GBCHR;
1721 case ANSI('B', ')'):
1722 compatibility(VT100);
1723 if (!cfg.no_remote_charset)
1724 cset_attr[1] = ATTR_ASCII;
1726 case ANSI('0', ')'):
1727 compatibility(VT100);
1728 if (!cfg.no_remote_charset)
1729 cset_attr[1] = ATTR_LINEDRW;
1731 case ANSI('U', ')'):
1732 compatibility(OTHER);
1733 if (!cfg.no_remote_charset)
1734 cset_attr[1] = ATTR_SCOACS;
1737 case ANSI('8', '%'): /* Old Linux code */
1738 case ANSI('G', '%'):
1739 compatibility(OTHER);
1740 if (!cfg.no_remote_charset)
1743 case ANSI('@', '%'):
1744 compatibility(OTHER);
1745 if (!cfg.no_remote_charset)
1751 termstate = TOPLEVEL; /* default */
1753 if (esc_nargs <= ARGS_MAX) {
1754 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1755 esc_args[esc_nargs - 1] = 0;
1756 esc_args[esc_nargs - 1] =
1757 10 * esc_args[esc_nargs - 1] + c - '0';
1759 termstate = SEEN_CSI;
1760 } else if (c == ';') {
1761 if (++esc_nargs <= ARGS_MAX)
1762 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1763 termstate = SEEN_CSI;
1764 } else if (c < '@') {
1771 termstate = SEEN_CSI;
1773 switch (ANSI(c, esc_query)) {
1774 case 'A': /* move up N lines */
1775 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1776 seen_disp_event = TRUE;
1778 case 'e': /* move down N lines */
1779 compatibility(ANSI);
1782 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1783 seen_disp_event = TRUE;
1785 case ANSI('c', '>'): /* report xterm version */
1786 compatibility(OTHER);
1787 /* this reports xterm version 136 so that VIM can
1788 use the drag messages from the mouse reporting */
1789 ldisc_send("\033[>0;136;0c", 11, 0);
1791 case 'a': /* move right N cols */
1792 compatibility(ANSI);
1795 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1796 seen_disp_event = TRUE;
1798 case 'D': /* move left N cols */
1799 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1800 seen_disp_event = TRUE;
1802 case 'E': /* move down N lines and CR */
1803 compatibility(ANSI);
1804 move(0, curs.y + def(esc_args[0], 1), 1);
1805 seen_disp_event = TRUE;
1807 case 'F': /* move up N lines and CR */
1808 compatibility(ANSI);
1809 move(0, curs.y - def(esc_args[0], 1), 1);
1810 seen_disp_event = TRUE;
1813 case '`': /* set horizontal posn */
1814 compatibility(ANSI);
1815 move(def(esc_args[0], 1) - 1, curs.y, 0);
1816 seen_disp_event = TRUE;
1818 case 'd': /* set vertical posn */
1819 compatibility(ANSI);
1821 (dec_om ? marg_t : 0) + def(esc_args[0],
1824 seen_disp_event = TRUE;
1827 case 'f': /* set horz and vert posns at once */
1829 esc_args[1] = ARG_DEFAULT;
1830 move(def(esc_args[1], 1) - 1,
1831 (dec_om ? marg_t : 0) + def(esc_args[0],
1834 seen_disp_event = TRUE;
1836 case 'J': /* erase screen or parts of it */
1838 unsigned int i = def(esc_args[0], 0) + 1;
1841 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1844 seen_disp_event = TRUE;
1846 case 'K': /* erase line or parts of it */
1848 unsigned int i = def(esc_args[0], 0) + 1;
1851 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1853 seen_disp_event = TRUE;
1855 case 'L': /* insert lines */
1856 compatibility(VT102);
1857 if (curs.y <= marg_b)
1858 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1861 seen_disp_event = TRUE;
1863 case 'M': /* delete lines */
1864 compatibility(VT102);
1865 if (curs.y <= marg_b)
1866 scroll(curs.y, marg_b, def(esc_args[0], 1),
1869 seen_disp_event = TRUE;
1871 case '@': /* insert chars */
1872 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1873 compatibility(VT102);
1874 insch(def(esc_args[0], 1));
1875 seen_disp_event = TRUE;
1877 case 'P': /* delete chars */
1878 compatibility(VT102);
1879 insch(-def(esc_args[0], 1));
1880 seen_disp_event = TRUE;
1882 case 'c': /* terminal type query */
1883 compatibility(VT100);
1884 /* This is the response for a VT102 */
1885 ldisc_send(id_string, strlen(id_string), 0);
1887 case 'n': /* cursor position query */
1888 if (esc_args[0] == 6) {
1890 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1892 ldisc_send(buf, strlen(buf), 0);
1893 } else if (esc_args[0] == 5) {
1894 ldisc_send("\033[0n", 4, 0);
1897 case 'h': /* toggle modes to high */
1899 compatibility(VT100);
1902 for (i = 0; i < esc_nargs; i++)
1903 toggle_mode(esc_args[i], esc_query, TRUE);
1908 compatibility(VT100);
1910 if (esc_nargs != 1) break;
1911 if (esc_args[0] == 5 && *cfg.printer) {
1913 only_printing = !esc_query;
1916 } else if (esc_args[0] == 4 && printing) {
1917 term_print_finish();
1921 case 'l': /* toggle modes to low */
1923 compatibility(VT100);
1926 for (i = 0; i < esc_nargs; i++)
1927 toggle_mode(esc_args[i], esc_query, FALSE);
1930 case 'g': /* clear tabs */
1931 compatibility(VT100);
1932 if (esc_nargs == 1) {
1933 if (esc_args[0] == 0) {
1934 tabs[curs.x] = FALSE;
1935 } else if (esc_args[0] == 3) {
1937 for (i = 0; i < cols; i++)
1942 case 'r': /* set scroll margins */
1943 compatibility(VT100);
1944 if (esc_nargs <= 2) {
1946 top = def(esc_args[0], 1) - 1;
1947 bot = (esc_nargs <= 1
1949 0 ? rows : def(esc_args[1], rows)) - 1;
1952 /* VTTEST Bug 9 - if region is less than 2 lines
1953 * don't change region.
1955 if (bot - top > 0) {
1960 * I used to think the cursor should be
1961 * placed at the top of the newly marginned
1962 * area. Apparently not: VMS TPU falls over
1965 * Well actually it should for Origin mode - RDB
1967 curs.y = (dec_om ? marg_t : 0);
1969 seen_disp_event = TRUE;
1973 case 'm': /* set graphics rendition */
1976 * A VT100 without the AVO only had one attribute, either
1977 * underline or reverse video depending on the cursor type,
1978 * this was selected by CSI 7m.
1981 * This is sometimes DIM, eg on the GIGI and Linux
1983 * This is sometimes INVIS various ANSI.
1985 * This like 22 disables BOLD, DIM and INVIS
1987 * The ANSI colours appear on any terminal that has colour
1988 * (obviously) but the interaction between sgr0 and the
1989 * colours varies but is usually related to the background
1990 * colour erase item.
1991 * The interaction between colour attributes and the mono
1992 * ones is also very implementation dependent.
1994 * The 39 and 49 attributes are likely to be unimplemented.
1997 for (i = 0; i < esc_nargs; i++) {
1998 switch (def(esc_args[i], 0)) {
1999 case 0: /* restore defaults */
2000 curr_attr = ATTR_DEFAULT;
2002 case 1: /* enable bold */
2003 compatibility(VT100AVO);
2004 curr_attr |= ATTR_BOLD;
2006 case 21: /* (enable double underline) */
2007 compatibility(OTHER);
2008 case 4: /* enable underline */
2009 compatibility(VT100AVO);
2010 curr_attr |= ATTR_UNDER;
2012 case 5: /* enable blink */
2013 compatibility(VT100AVO);
2014 curr_attr |= ATTR_BLINK;
2016 case 7: /* enable reverse video */
2017 curr_attr |= ATTR_REVERSE;
2019 case 10: /* SCO acs off */
2020 compatibility(SCOANSI);
2021 if (cfg.no_remote_charset) break;
2023 case 11: /* SCO acs on */
2024 compatibility(SCOANSI);
2025 if (cfg.no_remote_charset) break;
2027 case 12: /* SCO acs on flipped */
2028 compatibility(SCOANSI);
2029 if (cfg.no_remote_charset) break;
2031 case 22: /* disable bold */
2032 compatibility2(OTHER, VT220);
2033 curr_attr &= ~ATTR_BOLD;
2035 case 24: /* disable underline */
2036 compatibility2(OTHER, VT220);
2037 curr_attr &= ~ATTR_UNDER;
2039 case 25: /* disable blink */
2040 compatibility2(OTHER, VT220);
2041 curr_attr &= ~ATTR_BLINK;
2043 case 27: /* disable reverse video */
2044 compatibility2(OTHER, VT220);
2045 curr_attr &= ~ATTR_REVERSE;
2056 curr_attr &= ~ATTR_FGMASK;
2058 (esc_args[i] - 30) << ATTR_FGSHIFT;
2060 case 39: /* default-foreground */
2061 curr_attr &= ~ATTR_FGMASK;
2062 curr_attr |= ATTR_DEFFG;
2073 curr_attr &= ~ATTR_BGMASK;
2075 (esc_args[i] - 40) << ATTR_BGSHIFT;
2077 case 49: /* default-background */
2078 curr_attr &= ~ATTR_BGMASK;
2079 curr_attr |= ATTR_DEFBG;
2084 erase_char = (' ' | ATTR_ASCII |
2086 (ATTR_FGMASK | ATTR_BGMASK)));
2089 case 's': /* save cursor */
2092 case 'u': /* restore cursor */
2094 seen_disp_event = TRUE;
2096 case 't': /* set page size - ie window height */
2098 * VT340/VT420 sequence DECSLPP, DEC only allows values
2099 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2100 * illegal values (eg first arg 1..9) for window changing
2104 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2105 compatibility(VT340TEXT);
2106 if (!cfg.no_remote_resize)
2107 request_resize(cols, def(esc_args[0], 24));
2109 } else if (esc_nargs >= 1 &&
2112 compatibility(OTHER);
2114 switch (esc_args[0]) {
2124 if (esc_nargs >= 3) {
2125 if (!cfg.no_remote_resize)
2126 move_window(def(esc_args[1], 0),
2127 def(esc_args[2], 0));
2131 /* We should resize the window to a given
2132 * size in pixels here, but currently our
2133 * resizing code isn't healthy enough to
2137 set_zorder(TRUE); /* move to top */
2140 set_zorder(FALSE); /* move to bottom */
2146 if (esc_nargs >= 3) {
2147 if (!cfg.no_remote_resize)
2148 request_resize(def(esc_args[2], cfg.width),
2149 def(esc_args[1], cfg.height));
2154 set_zoomed(esc_args[1] ? TRUE : FALSE);
2157 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2161 get_window_pos(&x, &y);
2162 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2163 ldisc_send(buf, len, 0);
2166 get_window_pixels(&x, &y);
2167 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2168 ldisc_send(buf, len, 0);
2171 len = sprintf(buf, "\033[8;%d;%dt",
2173 ldisc_send(buf, len, 0);
2177 * Hmmm. Strictly speaking we
2178 * should return `the size of the
2179 * screen in characters', but
2180 * that's not easy: (a) window
2181 * furniture being what it is it's
2182 * hard to compute, and (b) in
2183 * resize-font mode maximising the
2184 * window wouldn't change the
2185 * number of characters. *shrug*. I
2186 * think we'll ignore it for the
2187 * moment and see if anyone
2188 * complains, and then ask them
2189 * what they would like it to do.
2193 p = get_window_title(TRUE);
2195 ldisc_send("\033]L", 3, 0);
2196 ldisc_send(p, len, 0);
2197 ldisc_send("\033\\", 2, 0);
2200 p = get_window_title(FALSE);
2202 ldisc_send("\033]l", 3, 0);
2203 ldisc_send(p, len, 0);
2204 ldisc_send("\033\\", 2, 0);
2210 compatibility(SCOANSI);
2211 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2214 seen_disp_event = TRUE;
2217 compatibility(SCOANSI);
2218 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2221 seen_disp_event = TRUE;
2223 case ANSI('|', '*'):
2224 /* VT420 sequence DECSNLS
2225 * Set number of lines on screen
2226 * VT420 uses VGA like hardware and can support any size in
2227 * reasonable range (24..49 AIUI) with no default specified.
2229 compatibility(VT420);
2230 if (esc_nargs == 1 && esc_args[0] > 0) {
2231 if (!cfg.no_remote_resize)
2232 request_resize(cols, def(esc_args[0], cfg.height));
2236 case ANSI('|', '$'):
2237 /* VT340/VT420 sequence DECSCPP
2238 * Set number of columns per page
2239 * Docs imply range is only 80 or 132, but I'll allow any.
2241 compatibility(VT340TEXT);
2242 if (esc_nargs <= 1) {
2243 if (!cfg.no_remote_resize)
2244 request_resize(def(esc_args[0], cfg.width), rows);
2248 case 'X': /* write N spaces w/o moving cursor */
2249 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2250 compatibility(ANSIMIN);
2252 int n = def(esc_args[0], 1);
2254 unsigned long *p = cpos;
2255 if (n > cols - curs.x)
2259 check_selection(curs, cursplus);
2262 seen_disp_event = TRUE;
2265 case 'x': /* report terminal characteristics */
2266 compatibility(VT100);
2269 int i = def(esc_args[0], 0);
2270 if (i == 0 || i == 1) {
2271 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2273 ldisc_send(buf, 20, 0);
2277 case 'Z': /* BackTab for xterm */
2278 compatibility(OTHER);
2280 int i = def(esc_args[0], 1);
2281 pos old_curs = curs;
2283 for(;i>0 && curs.x>0; i--) {
2286 } while (curs.x >0 && !tabs[curs.x]);
2289 check_selection(old_curs, curs);
2292 case ANSI('L', '='):
2293 compatibility(OTHER);
2294 use_bce = (esc_args[0] <= 0);
2295 erase_char = ERASE_CHAR;
2297 erase_char = (' ' | ATTR_ASCII |
2299 (ATTR_FGMASK | ATTR_BGMASK)));
2301 case ANSI('E', '='):
2302 compatibility(OTHER);
2303 blink_is_real = (esc_args[0] >= 1);
2305 case ANSI('p', '"'):
2306 /* Allow the host to make this emulator a 'perfect' VT102.
2307 * This first appeared in the VT220, but we do need to get
2308 * back to PuTTY mode so I won't check it.
2310 * The arg in 40..42,50 are a PuTTY extension.
2311 * The 2nd arg, 8bit vs 7bit is not checked.
2313 * Setting VT102 mode should also change the Fkeys to
2314 * generate PF* codes as a real VT102 has no Fkeys.
2315 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2318 * Note ESC c will NOT change this!
2321 switch (esc_args[0]) {
2323 compatibility_level &= ~TM_VTXXX;
2324 compatibility_level |= TM_VT102;
2327 compatibility_level &= ~TM_VTXXX;
2328 compatibility_level |= TM_VT220;
2332 if (esc_args[0] > 60 && esc_args[0] < 70)
2333 compatibility_level |= TM_VTXXX;
2337 compatibility_level &= TM_VTXXX;
2340 compatibility_level = TM_PUTTY;
2343 compatibility_level = TM_SCOANSI;
2347 compatibility_level = TM_PUTTY;
2353 /* Change the response to CSI c */
2354 if (esc_args[0] == 50) {
2357 strcpy(id_string, "\033[?");
2358 for (i = 1; i < esc_nargs; i++) {
2360 strcat(id_string, ";");
2361 sprintf(lbuf, "%d", esc_args[i]);
2362 strcat(id_string, lbuf);
2364 strcat(id_string, "c");
2367 /* Is this a good idea ?
2368 * Well we should do a soft reset at this point ...
2370 if (!has_compat(VT420) && has_compat(VT100)) {
2371 if (!cfg.no_remote_resize) {
2373 request_resize(132, 24);
2375 request_resize(80, 24);
2385 case 'P': /* Linux palette sequence */
2386 termstate = SEEN_OSC_P;
2389 case 'R': /* Linux palette reset */
2392 termstate = TOPLEVEL;
2394 case 'W': /* word-set */
2395 termstate = SEEN_OSC_W;
2408 esc_args[0] = 10 * esc_args[0] + c - '0';
2412 * Grotty hack to support xterm and DECterm title
2413 * sequences concurrently.
2415 if (esc_args[0] == 2) {
2419 /* else fall through */
2421 termstate = OSC_STRING;
2427 * This OSC stuff is EVIL. It takes just one character to get into
2428 * sysline mode and it's not initially obvious how to get out.
2429 * So I've added CR and LF as string aborts.
2430 * This shouldn't effect compatibility as I believe embedded
2431 * control characters are supposed to be interpreted (maybe?)
2432 * and they don't display anything useful anyway.
2436 if (c == '\n' || c == '\r') {
2437 termstate = TOPLEVEL;
2438 } else if (c == 0234 || c == '\007') {
2440 * These characters terminate the string; ST and BEL
2441 * terminate the sequence and trigger instant
2442 * processing of it, whereas ESC goes back to SEEN_ESC
2443 * mode unless it is followed by \, in which case it is
2444 * synonymous with ST in the first place.
2447 termstate = TOPLEVEL;
2448 } else if (c == '\033')
2449 termstate = OSC_MAYBE_ST;
2450 else if (osc_strlen < OSC_STR_MAX)
2451 osc_string[osc_strlen++] = c;
2455 int max = (osc_strlen == 0 ? 21 : 16);
2457 if (c >= '0' && c <= '9')
2459 else if (c >= 'A' && c <= 'A' + max - 10)
2461 else if (c >= 'a' && c <= 'a' + max - 10)
2464 termstate = TOPLEVEL;
2467 osc_string[osc_strlen++] = val;
2468 if (osc_strlen >= 7) {
2469 palette_set(osc_string[0],
2470 osc_string[1] * 16 + osc_string[2],
2471 osc_string[3] * 16 + osc_string[4],
2472 osc_string[5] * 16 + osc_string[6]);
2474 termstate = TOPLEVEL;
2490 esc_args[0] = 10 * esc_args[0] + c - '0';
2493 termstate = OSC_STRING;
2498 termstate = TOPLEVEL;
2499 seen_disp_event = TRUE;
2502 move(curs.x, curs.y - 1, 1);
2505 move(curs.x, curs.y + 1, 1);
2508 move(curs.x + 1, curs.y, 1);
2511 move(curs.x - 1, curs.y, 1);
2514 * From the VT100 Manual
2515 * NOTE: The special graphics characters in the VT100
2516 * are different from those in the VT52
2518 * From VT102 manual:
2519 * 137 _ Blank - Same
2520 * 140 ` Reserved - Humm.
2521 * 141 a Solid rectangle - Similar
2522 * 142 b 1/ - Top half of fraction for the
2523 * 143 c 3/ - subscript numbers below.
2526 * 146 f Degrees - Same
2527 * 147 g Plus or minus - Same
2529 * 151 i Ellipsis (dots)
2532 * 154 l Bar at scan 0
2533 * 155 m Bar at scan 1
2534 * 156 n Bar at scan 2
2535 * 157 o Bar at scan 3 - Similar
2536 * 160 p Bar at scan 4 - Similar
2537 * 161 q Bar at scan 5 - Similar
2538 * 162 r Bar at scan 6 - Same
2539 * 163 s Bar at scan 7 - Similar
2554 cset_attr[cset = 0] = ATTR_LINEDRW;
2557 cset_attr[cset = 0] = ATTR_ASCII;
2564 scroll(0, rows - 1, -1, TRUE);
2565 else if (curs.y > 0)
2571 erase_lots(FALSE, FALSE, TRUE);
2575 erase_lots(TRUE, FALSE, TRUE);
2579 /* XXX Print cursor line */
2582 /* XXX Start controller mode */
2585 /* XXX Stop controller mode */
2589 termstate = VT52_Y1;
2592 ldisc_send("\033/Z", 3, 0);
2595 app_keypad_keys = TRUE;
2598 app_keypad_keys = FALSE;
2601 /* XXX This should switch to VT100 mode not current or default
2602 * VT mode. But this will only have effect in a VT220+
2606 blink_is_real = cfg.blinktext;
2610 /* XXX Enter auto print mode */
2613 /* XXX Exit auto print mode */
2616 /* XXX Print screen */
2622 /* compatibility(ATARI) */
2624 erase_lots(FALSE, FALSE, TRUE);
2628 /* compatibility(ATARI) */
2629 if (curs.y <= marg_b)
2630 scroll(curs.y, marg_b, -1, FALSE);
2633 /* compatibility(ATARI) */
2634 if (curs.y <= marg_b)
2635 scroll(curs.y, marg_b, 1, TRUE);
2638 /* compatibility(ATARI) */
2639 termstate = VT52_FG;
2642 /* compatibility(ATARI) */
2643 termstate = VT52_BG;
2646 /* compatibility(ATARI) */
2647 erase_lots(FALSE, TRUE, FALSE);
2651 /* compatibility(ATARI) */
2655 /* compatibility(ATARI) */
2658 /* case 'j': Save cursor position - broken on ST */
2659 /* case 'k': Restore cursor position */
2661 /* compatibility(ATARI) */
2662 erase_lots(TRUE, TRUE, TRUE);
2668 /* compatibility(ATARI) */
2669 erase_lots(TRUE, TRUE, FALSE);
2672 /* compatibility(ATARI) */
2673 curr_attr |= ATTR_REVERSE;
2676 /* compatibility(ATARI) */
2677 curr_attr &= ~ATTR_REVERSE;
2679 case 'v': /* wrap Autowrap on - Wyse style */
2680 /* compatibility(ATARI) */
2683 case 'w': /* Autowrap off */
2684 /* compatibility(ATARI) */
2689 /* compatibility(OTHER) */
2691 curr_attr = ATTR_DEFAULT;
2693 erase_char = (' ' | ATTR_ASCII |
2695 (ATTR_FGMASK | ATTR_BGMASK)));
2698 /* compatibility(VI50) */
2699 curr_attr |= ATTR_UNDER;
2702 /* compatibility(VI50) */
2703 curr_attr &= ~ATTR_UNDER;
2706 /* compatibility(VI50) */
2708 curr_attr |= ATTR_BOLD;
2711 /* compatibility(VI50) */
2713 curr_attr &= ~ATTR_BOLD;
2719 termstate = VT52_Y2;
2720 move(curs.x, c - ' ', 0);
2723 termstate = TOPLEVEL;
2724 move(c - ' ', curs.y, 0);
2729 termstate = TOPLEVEL;
2730 curr_attr &= ~ATTR_FGMASK;
2731 curr_attr &= ~ATTR_BOLD;
2732 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2733 if ((c & 0x8) || vt52_bold)
2734 curr_attr |= ATTR_BOLD;
2737 erase_char = (' ' | ATTR_ASCII |
2738 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2741 termstate = TOPLEVEL;
2742 curr_attr &= ~ATTR_BGMASK;
2743 curr_attr &= ~ATTR_BLINK;
2744 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2746 /* Note: bold background */
2748 curr_attr |= ATTR_BLINK;
2751 erase_char = (' ' | ATTR_ASCII |
2752 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2755 default: break; /* placate gcc warning about enum use */
2757 if (selstate != NO_SELECTION) {
2758 pos cursplus = curs;
2760 check_selection(curs, cursplus);
2769 * Compare two lines to determine whether they are sufficiently
2770 * alike to scroll-optimise one to the other. Return the degree of
2773 static int linecmp(unsigned long *a, unsigned long *b)
2777 for (i = n = 0; i < cols; i++)
2778 n += (*a++ == *b++);
2784 * Given a context, update the window. Out of paranoia, we don't
2785 * allow WM_PAINT responses to do scrolling optimisations.
2787 static void do_paint(Context ctx, int may_optimise)
2789 int i, j, our_curs_y;
2790 unsigned long rv, cursor;
2793 long cursor_background = ERASE_CHAR;
2794 unsigned long ticks;
2797 * Check the visual bell state.
2800 ticks = GETTICKCOUNT();
2801 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2805 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2808 * screen array, disptop, scrtop,
2810 * cfg.blinkpc, blink_is_real, tblinker,
2811 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2814 /* Has the cursor position or type changed ? */
2817 if (blinker || !cfg.blink_cur)
2818 cursor = TATTR_ACTCURS;
2822 cursor = TATTR_PASCURS;
2824 cursor |= TATTR_RIGHTCURS;
2827 our_curs_y = curs.y - disptop;
2829 if (dispcurs && (curstype != cursor ||
2831 disptext + our_curs_y * (cols + 1) + curs.x)) {
2832 if (dispcurs > disptext &&
2833 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2834 dispcurs[-1] |= ATTR_INVALID;
2835 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2836 dispcurs[1] |= ATTR_INVALID;
2837 *dispcurs |= ATTR_INVALID;
2842 /* The normal screen data */
2843 for (i = 0; i < rows; i++) {
2844 unsigned long *ldata;
2846 int idx, dirty_line, dirty_run, selected;
2847 unsigned long attr = 0;
2848 int updated_line = 0;
2851 int last_run_dirty = 0;
2853 scrpos.y = i + disptop;
2854 ldata = lineptr(scrpos.y);
2855 lattr = (ldata[cols] & LATTR_MODE);
2857 idx = i * (cols + 1);
2858 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2859 disptext[idx + cols] = ldata[cols];
2861 for (j = 0; j < cols; j++, idx++) {
2862 unsigned long tattr, tchar;
2863 unsigned long *d = ldata + j;
2867 tchar = (*d & (CHAR_MASK | CSET_MASK));
2868 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2869 switch (tchar & CSET_MASK) {
2871 tchar = unitab_line[tchar & 0xFF];
2874 tchar = unitab_xterm[tchar & 0xFF];
2877 tchar = unitab_scoacs[tchar&0xFF];
2880 tattr |= (tchar & CSET_MASK);
2882 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2885 /* Video reversing things */
2886 if (seltype == LEXICOGRAPHIC)
2887 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2889 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2891 ^ (selected ? ATTR_REVERSE : 0));
2893 /* 'Real' blinking ? */
2894 if (blink_is_real && (tattr & ATTR_BLINK)) {
2895 if (has_focus && tblinker) {
2897 tattr &= ~CSET_MASK;
2900 tattr &= ~ATTR_BLINK;
2904 * Check the font we'll _probably_ be using to see if
2905 * the character is wide when we don't want it to be.
2907 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2908 if ((tattr & ATTR_WIDE) == 0 &&
2909 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2910 tattr |= ATTR_NARROW;
2911 } else if (disptext[idx]&ATTR_NARROW)
2912 tattr |= ATTR_NARROW;
2914 /* Cursor here ? Save the 'background' */
2915 if (i == our_curs_y && j == curs.x) {
2916 cursor_background = tattr | tchar;
2917 dispcurs = disptext + idx;
2920 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2923 break_run = (tattr != attr || j - start >= sizeof(ch));
2925 /* Special hack for VT100 Linedraw glyphs */
2926 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2927 && tchar <= 0xBD) break_run = TRUE;
2929 if (!dbcs_screenfont && !dirty_line) {
2930 if ((tchar | tattr) == disptext[idx])
2932 else if (!dirty_run && ccount == 1)
2937 if ((dirty_run || last_run_dirty) && ccount > 0) {
2938 do_text(ctx, start, i, ch, ccount, attr, lattr);
2944 if (dbcs_screenfont)
2945 last_run_dirty = dirty_run;
2946 dirty_run = dirty_line;
2949 if ((tchar | tattr) != disptext[idx])
2951 ch[ccount++] = (char) tchar;
2952 disptext[idx] = tchar | tattr;
2954 /* If it's a wide char step along to the next one. */
2955 if (tattr & ATTR_WIDE) {
2959 /* Cursor is here ? Ouch! */
2960 if (i == our_curs_y && j == curs.x) {
2961 cursor_background = *d;
2962 dispcurs = disptext + idx;
2964 if (disptext[idx] != *d)
2970 if (dirty_run && ccount > 0) {
2971 do_text(ctx, start, i, ch, ccount, attr, lattr);
2975 /* Cursor on this line ? (and changed) */
2976 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2977 ch[0] = (char) (cursor_background & CHAR_MASK);
2978 attr = (cursor_background & ATTR_MASK) | cursor;
2979 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2986 * Flick the switch that says if blinking things should be shown or hidden.
2989 void term_blink(int flg)
2991 static long last_blink = 0;
2992 static long last_tblink = 0;
2993 long now, blink_diff;
2995 now = GETTICKCOUNT();
2996 blink_diff = now - last_tblink;
2998 /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
2999 if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
3001 tblinker = !tblinker;
3010 blink_diff = now - last_blink;
3012 /* Make sure the cursor blinks no faster than system blink rate */
3013 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3021 * Invalidate the whole screen so it will be repainted in full.
3023 void term_invalidate(void)
3027 for (i = 0; i < rows * (cols + 1); i++)
3028 disptext[i] = ATTR_INVALID;
3032 * Paint the window in response to a WM_PAINT message.
3034 void term_paint(Context ctx, int left, int top, int right, int bottom)
3037 if (left < 0) left = 0;
3038 if (top < 0) top = 0;
3039 if (right >= cols) right = cols-1;
3040 if (bottom >= rows) bottom = rows-1;
3042 for (i = top; i <= bottom && i < rows; i++) {
3043 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
3044 for (j = left; j <= right && j < cols; j++)
3045 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3047 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
3048 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3051 /* This should happen soon enough, also for some reason it sometimes
3052 * fails to actually do anything when re-sizing ... painting the wrong
3056 do_paint (ctx, FALSE);
3060 * Attempt to scroll the scrollback. The second parameter gives the
3061 * position we want to scroll to; the first is +1 to denote that
3062 * this position is relative to the beginning of the scrollback, -1
3063 * to denote it is relative to the end, and 0 to denote that it is
3064 * relative to the current position.
3066 void term_scroll(int rel, int where)
3068 int sbtop = -count234(scrollback);
3070 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
3071 if (disptop < sbtop)
3079 static void clipme(pos top, pos bottom, int rect)
3082 wchar_t *wbptr; /* where next char goes within workbuf */
3084 int wblen = 0; /* workbuf len */
3085 int buflen; /* amount of memory allocated to workbuf */
3087 buflen = 5120; /* Default size */
3088 workbuf = smalloc(buflen * sizeof(wchar_t));
3089 wbptr = workbuf; /* start filling here */
3090 old_top_x = top.x; /* needed for rect==1 */
3092 while (poslt(top, bottom)) {
3094 unsigned long *ldata = lineptr(top.y);
3098 * nlpos will point at the maximum position on this line we
3099 * should copy up to. So we start it at the end of the
3106 * ... move it backwards if there's unused space at the end
3107 * of the line (and also set `nl' if this is the case,
3108 * because in normal selection mode this means we need a
3109 * newline at the end)...
3111 if (!(ldata[cols] & LATTR_WRAPPED)) {
3112 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3113 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3114 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3115 && poslt(top, nlpos))
3117 if (poslt(nlpos, bottom))
3122 * ... and then clip it to the terminal x coordinate if
3123 * we're doing rectangular selection. (In this case we
3124 * still did the above, so that copying e.g. the right-hand
3125 * column from a table doesn't fill with spaces on the
3129 if (nlpos.x > bottom.x)
3131 nl = (top.y < bottom.y);
3134 while (poslt(top, bottom) && poslt(top, nlpos)) {
3137 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3139 wchar_t cbuf[16], *p;
3140 int uc = (ldata[top.x] & 0xFFFF);
3143 if (uc == UCSWIDE) {
3148 switch (uc & CSET_MASK) {
3151 uc = unitab_xterm[uc & 0xFF];
3155 uc = unitab_line[uc & 0xFF];
3158 uc = unitab_scoacs[uc&0xFF];
3161 switch (uc & CSET_MASK) {
3163 uc = unitab_font[uc & 0xFF];
3166 uc = unitab_oemcp[uc & 0xFF];
3170 set = (uc & CSET_MASK);
3171 c = (uc & CHAR_MASK);
3175 if (DIRECT_FONT(uc)) {
3176 if (c >= ' ' && c != 0x7F) {
3177 unsigned char buf[4];
3180 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3182 buf[1] = (unsigned char) ldata[top.x + 1];
3183 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3187 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3191 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3198 for (p = cbuf; *p; p++) {
3199 /* Enough overhead for trailing NL and nul */
3200 if (wblen >= buflen - 16) {
3203 sizeof(wchar_t) * (buflen += 100));
3204 wbptr = workbuf + wblen;
3213 for (i = 0; i < sel_nl_sz; i++) {
3215 *wbptr++ = sel_nl[i];
3219 top.x = rect ? old_top_x : 0;
3221 #if SELECTION_NUL_TERMINATED
3225 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3226 if (buflen > 0) /* indicates we allocated this buffer */
3230 void term_copyall(void)
3233 top.y = -count234(scrollback);
3235 clipme(top, curs, 0);
3239 * The wordness array is mainly for deciding the disposition of the US-ASCII
3242 static int wordtype(int uc)
3245 int start, end, ctype;
3246 } *wptr, ucs_words[] = {
3252 0x037e, 0x037e, 1}, /* Greek question mark */
3254 0x0387, 0x0387, 1}, /* Greek ano teleia */
3256 0x055a, 0x055f, 1}, /* Armenian punctuation */
3258 0x0589, 0x0589, 1}, /* Armenian full stop */
3260 0x0700, 0x070d, 1}, /* Syriac punctuation */
3262 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3264 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3266 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3268 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3270 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3272 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3274 0x2000, 0x200a, 0}, /* Various spaces */
3276 0x2070, 0x207f, 2}, /* superscript */
3278 0x2080, 0x208f, 2}, /* subscript */
3280 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3282 0x3000, 0x3000, 0}, /* ideographic space */
3284 0x3001, 0x3020, 1}, /* ideographic punctuation */
3286 0x303f, 0x309f, 3}, /* Hiragana */
3288 0x30a0, 0x30ff, 3}, /* Katakana */
3290 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3292 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3294 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3296 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3298 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3300 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3302 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3304 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3306 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3311 uc &= (CSET_MASK | CHAR_MASK);
3313 switch (uc & CSET_MASK) {
3315 uc = unitab_xterm[uc & 0xFF];
3318 uc = unitab_line[uc & 0xFF];
3321 uc = unitab_scoacs[uc&0xFF];
3324 switch (uc & CSET_MASK) {
3326 uc = unitab_font[uc & 0xFF];
3329 uc = unitab_oemcp[uc & 0xFF];
3333 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3334 * fail as there's such a thing as a double width space. :-(
3336 if (dbcs_screenfont && font_codepage == line_codepage)
3340 return wordness[uc];
3342 for (wptr = ucs_words; wptr->start; wptr++) {
3343 if (uc >= wptr->start && uc <= wptr->end)
3351 * Spread the selection outwards according to the selection mode.
3353 static pos sel_spread_half(pos p, int dir)
3355 unsigned long *ldata;
3357 int topy = -count234(scrollback);
3359 ldata = lineptr(p.y);
3364 * In this mode, every character is a separate unit, except
3365 * for runs of spaces at the end of a non-wrapping line.
3367 if (!(ldata[cols] & LATTR_WRAPPED)) {
3368 unsigned long *q = ldata + cols;
3369 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3371 if (q == ldata + cols)
3373 if (p.x >= q - ldata)
3374 p.x = (dir == -1 ? q - ldata : cols - 1);
3379 * In this mode, the units are maximal runs of characters
3380 * whose `wordness' has the same value.
3382 wvalue = wordtype(ldata[p.x]);
3386 if (wordtype(ldata[p.x + 1]) == wvalue)
3391 if (ldata[cols] & LATTR_WRAPPED) {
3392 unsigned long *ldata2;
3393 ldata2 = lineptr(p.y+1);
3394 if (wordtype(ldata2[0]) == wvalue) {
3407 if (wordtype(ldata[p.x - 1]) == wvalue)
3412 unsigned long *ldata2;
3415 ldata2 = lineptr(p.y-1);
3416 if ((ldata2[cols] & LATTR_WRAPPED) &&
3417 wordtype(ldata2[cols-1]) == wvalue) {
3429 * In this mode, every line is a unit.
3431 p.x = (dir == -1 ? 0 : cols - 1);
3437 static void sel_spread(void)
3439 if (seltype == LEXICOGRAPHIC) {
3440 selstart = sel_spread_half(selstart, -1);
3442 selend = sel_spread_half(selend, +1);
3447 void term_do_paste(void)
3452 get_clip(&data, &len);
3457 sfree(paste_buffer);
3458 paste_pos = paste_hold = paste_len = 0;
3459 paste_buffer = smalloc(len * sizeof(wchar_t));
3462 while (p < data + len) {
3463 while (p < data + len &&
3464 !(p <= data + len - sel_nl_sz &&
3465 !memcmp(p, sel_nl, sizeof(sel_nl))))
3470 for (i = 0; i < p - q; i++) {
3471 paste_buffer[paste_len++] = q[i];
3475 if (p <= data + len - sel_nl_sz &&
3476 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3477 paste_buffer[paste_len++] = '\r';
3483 /* Assume a small paste will be OK in one go. */
3484 if (paste_len < 256) {
3485 luni_send(paste_buffer, paste_len, 0);
3487 sfree(paste_buffer);
3489 paste_pos = paste_hold = paste_len = 0;
3492 get_clip(NULL, NULL);
3495 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3496 int shift, int ctrl, int alt)
3499 unsigned long *ldata;
3500 int raw_mouse = (xterm_mouse &&
3501 !cfg.no_mouse_rep &&
3502 !(cfg.mouse_override && shift));
3503 int default_seltype;
3507 if (a == MA_DRAG && !raw_mouse)
3512 if (a == MA_DRAG && !raw_mouse)
3525 selpoint.y = y + disptop;
3527 ldata = lineptr(selpoint.y);
3528 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3532 int encstate = 0, r, c;
3534 static int is_down = 0;
3538 encstate = 0x20; /* left button down */
3549 case MBT_WHEEL_DOWN:
3552 default: break; /* placate gcc warning about enum use */
3556 if (xterm_mouse == 1)
3569 default: break; /* placate gcc warning about enum use */
3578 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3579 ldisc_send(abuf, 6, 0);
3583 b = translate_button(b);
3586 * Set the selection type (rectangular or normal) at the start
3587 * of a selection attempt, from the state of Alt.
3589 if (!alt ^ !cfg.rect_select)
3590 default_seltype = RECTANGULAR;
3592 default_seltype = LEXICOGRAPHIC;
3594 if (selstate == NO_SELECTION) {
3595 seltype = default_seltype;
3598 if (b == MBT_SELECT && a == MA_CLICK) {
3600 selstate = ABOUT_TO;
3601 seltype = default_seltype;
3602 selanchor = selpoint;
3604 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3606 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3607 selstate = DRAGGING;
3608 selstart = selanchor = selpoint;
3612 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3613 (b == MBT_EXTEND && a != MA_RELEASE)) {
3614 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3616 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3617 if (seltype == LEXICOGRAPHIC) {
3619 * For normal selection, we extend by moving
3620 * whichever end of the current selection is closer
3623 if (posdiff(selpoint, selstart) <
3624 posdiff(selend, selstart) / 2) {
3628 selanchor = selstart;
3632 * For rectangular selection, we have a choice of
3633 * _four_ places to put selanchor and selpoint: the
3634 * four corners of the selection.
3636 if (2*selpoint.x < selstart.x + selend.x)
3637 selanchor.x = selend.x-1;
3639 selanchor.x = selstart.x;
3641 if (2*selpoint.y < selstart.y + selend.y)
3642 selanchor.y = selend.y;
3644 selanchor.y = selstart.y;
3646 selstate = DRAGGING;
3648 if (selstate != ABOUT_TO && selstate != DRAGGING)
3649 selanchor = selpoint;
3650 selstate = DRAGGING;
3651 if (seltype == LEXICOGRAPHIC) {
3653 * For normal selection, we set (selstart,selend) to
3654 * (selpoint,selanchor) in some order.
3656 if (poslt(selpoint, selanchor)) {
3657 selstart = selpoint;
3661 selstart = selanchor;
3667 * For rectangular selection, we may need to
3668 * interchange x and y coordinates (if the user has
3669 * dragged in the -x and +y directions, or vice versa).
3671 selstart.x = min(selanchor.x, selpoint.x);
3672 selend.x = 1+max(selanchor.x, selpoint.x);
3673 selstart.y = min(selanchor.y, selpoint.y);
3674 selend.y = max(selanchor.y, selpoint.y);
3677 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3678 if (selstate == DRAGGING) {
3680 * We've completed a selection. We now transfer the
3681 * data to the clipboard.
3683 clipme(selstart, selend, (seltype == RECTANGULAR));
3684 selstate = SELECTED;
3686 selstate = NO_SELECTION;
3687 } else if (b == MBT_PASTE
3689 #if MULTICLICK_ONLY_EVENT
3690 || a == MA_2CLK || a == MA_3CLK
3703 sfree(paste_buffer);
3708 int term_paste_pending(void)
3710 return paste_len != 0;
3715 static long last_paste = 0;
3716 long now, paste_diff;
3721 /* Don't wait forever to paste */
3723 now = GETTICKCOUNT();
3724 paste_diff = now - last_paste;
3725 if (paste_diff >= 0 && paste_diff < 450)
3730 while (paste_pos < paste_len) {
3732 while (n + paste_pos < paste_len) {
3733 if (paste_buffer[paste_pos + n++] == '\r')
3736 luni_send(paste_buffer + paste_pos, n, 0);
3739 if (paste_pos < paste_len) {
3744 sfree(paste_buffer);
3749 static void deselect(void)
3751 selstate = NO_SELECTION;
3752 selstart.x = selstart.y = selend.x = selend.y = 0;
3755 void term_deselect(void)
3761 int term_ldisc(int option)
3763 if (option == LD_ECHO)
3764 return term_echoing;
3765 if (option == LD_EDIT)
3766 return term_editing;
3771 * from_backend(), to get data from the backend for the terminal.
3773 int from_backend(int is_stderr, char *data, int len)
3777 bufchain_add(&inbuf, data, len);
3780 * term_out() always completely empties inbuf. Therefore,
3781 * there's no reason at all to return anything other than zero
3782 * from this function, because there _can't_ be a question of
3783 * the remote side needing to wait until term_out() has cleared
3786 * This is a slightly suboptimal way to deal with SSH2 - in
3787 * principle, the window mechanism would allow us to continue
3788 * to accept data on forwarded ports and X connections even
3789 * while the terminal processing was going slowly - but we
3790 * can't do the 100% right thing without moving the terminal
3791 * processing into a separate thread, and that might hurt
3792 * portability. So we manage stdout buffering the old SSH1 way:
3793 * if the terminal processing goes slowly, the whole SSH
3794 * connection stops accepting data until it's ready.
3796 * In practice, I can't imagine this causing serious trouble.