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_disp_event && cfg.scroll_on_disp) {
332 disptop = 0; /* return to main screen */
334 need_sbar_update = TRUE;
336 if (need_sbar_update)
339 sys_cursor(curs.x, curs.y - disptop);
345 * Called from front end when a keypress occurs, to trigger
346 * anything magical that needs to happen in that situation.
348 void term_seen_key_event(void)
351 * On any keypress, clear the bell overload mechanism
352 * completely, on the grounds that large numbers of
353 * beeps coming from deliberate key action are likely
354 * to be intended (e.g. beeps from filename completion
355 * blocking repeatedly).
357 beep_overloaded = FALSE;
359 struct beeptime *tmp = beephead;
360 beephead = tmp->next;
367 * Reset the scrollback on keypress, if we're doing that.
369 if (cfg.scroll_on_key) {
370 disptop = 0; /* return to main screen */
376 * Same as power_on(), but an external function.
378 void term_pwron(void)
388 * When the user reconfigures us, we need to check the forbidden-
389 * alternate-screen config option, disable raw mouse mode if the
390 * user has disabled mouse reporting, and abandon a print job if
391 * the user has disabled printing.
393 void term_reconfig(void)
395 if (cfg.no_alt_screen)
397 if (cfg.no_mouse_rep) {
399 set_raw_mouse_mode(0);
401 if (cfg.no_remote_charset) {
402 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
403 sco_acs = alt_sco_acs = 0;
412 * Clear the scrollback.
414 void term_clrsb(void)
418 while ((line = delpos234(scrollback, 0)) != NULL) {
425 * Initialise the terminal.
429 screen = alt_screen = scrollback = NULL;
431 disptext = dispcurs = NULL;
436 beephead = beeptail = NULL;
439 beep_overloaded = FALSE;
443 * Set up the terminal for a given size.
445 void term_size(int newrows, int newcols, int newsavelines)
448 unsigned long *newdisp, *line;
451 int save_alt_which = alt_which;
453 if (newrows == rows && newcols == cols && newsavelines == savelines)
454 return; /* nothing to do */
460 alt_b = marg_b = newrows - 1;
463 scrollback = newtree234(NULL);
464 screen = newtree234(NULL);
469 * Resize the screen and scrollback. We only need to shift
470 * lines around within our data structures, because lineptr()
471 * will take care of resizing each individual line if
474 * - If the new screen and the old screen differ in length, we
475 * must shunt some lines in from the scrollback or out to
478 * - If doing that fails to provide us with enough material to
479 * fill the new screen (i.e. the number of rows needed in
480 * the new screen exceeds the total number in the previous
481 * screen+scrollback), we must invent some blank lines to
484 * - Then, if the new scrollback length is less than the
485 * amount of scrollback we actually have, we must throw some
488 sblen = count234(scrollback);
489 /* Do this loop to expand the screen if newrows > rows */
490 for (i = rows; i < newrows; i++) {
492 line = delpos234(scrollback, --sblen);
494 line = smalloc(TSIZE * (newcols + 2));
496 for (j = 0; j <= newcols; j++)
497 line[j + 1] = ERASE_CHAR;
499 addpos234(screen, line, 0);
501 /* Do this loop to shrink the screen if newrows < rows */
502 for (i = newrows; i < rows; i++) {
503 line = delpos234(screen, 0);
504 addpos234(scrollback, line, sblen++);
506 assert(count234(screen) == newrows);
507 while (sblen > newsavelines) {
508 line = delpos234(scrollback, 0);
512 assert(count234(scrollback) <= newsavelines);
515 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
516 for (i = 0; i < newrows * (newcols + 1); i++)
517 newdisp[i] = ATTR_INVALID;
522 newalt = newtree234(NULL);
523 for (i = 0; i < newrows; i++) {
524 line = smalloc(TSIZE * (newcols + 2));
526 for (j = 0; j <= newcols; j++)
527 line[j + 1] = erase_char;
528 addpos234(newalt, line, i);
531 while (NULL != (line = delpos234(alt_screen, 0)))
533 freetree234(alt_screen);
537 tabs = srealloc(tabs, newcols * sizeof(*tabs));
540 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
541 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
545 curs.y += newrows - rows;
548 if (curs.y >= newrows)
549 curs.y = newrows - 1;
550 if (curs.x >= newcols)
551 curs.x = newcols - 1;
553 wrapnext = alt_wnext = FALSE;
557 savelines = newsavelines;
560 swap_screen(save_alt_which);
570 static void swap_screen(int which)
575 if (which == alt_which)
602 wrapnext = alt_wnext;
614 sco_acs = alt_sco_acs;
621 * Update the scroll bar.
623 static void update_sbar(void)
627 nscroll = count234(scrollback);
629 set_sbar(nscroll + rows, nscroll + disptop, rows);
633 * Check whether the region bounded by the two pointers intersects
634 * the scroll region, and de-select the on-screen selection if so.
636 static void check_selection(pos from, pos to)
638 if (poslt(from, selend) && poslt(selstart, to))
643 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
644 * for backward.) `sb' is TRUE if the scrolling is permitted to
645 * affect the scrollback buffer.
647 * NB this function invalidates all pointers into lines of the
648 * screen data structures. In particular, you MUST call fix_cpos
649 * after calling scroll() and before doing anything else that
650 * uses the cpos shortcut pointer.
652 static void scroll(int topline, int botline, int lines, int sb)
654 unsigned long *line, *line2;
657 if (topline != 0 || alt_which != 0)
662 line = delpos234(screen, botline);
663 line = resizeline(line, cols);
664 for (i = 0; i < cols; i++)
665 line[i + 1] = erase_char;
667 addpos234(screen, line, topline);
669 if (selstart.y >= topline && selstart.y <= botline) {
671 if (selstart.y > botline) {
672 selstart.y = botline;
676 if (selend.y >= topline && selend.y <= botline) {
678 if (selend.y > botline) {
688 line = delpos234(screen, topline);
689 if (sb && savelines > 0) {
690 int sblen = count234(scrollback);
692 * We must add this line to the scrollback. We'll
693 * remove a line from the top of the scrollback to
694 * replace it, or allocate a new one if the
695 * scrollback isn't full.
697 if (sblen == savelines) {
698 sblen--, line2 = delpos234(scrollback, 0);
700 line2 = smalloc(TSIZE * (cols + 2));
703 addpos234(scrollback, line, sblen);
707 * If the user is currently looking at part of the
708 * scrollback, and they haven't enabled any options
709 * that are going to reset the scrollback as a
710 * result of this movement, then the chances are
711 * they'd like to keep looking at the same line. So
712 * we move their viewpoint at the same rate as the
713 * scroll, at least until their viewpoint hits the
714 * top end of the scrollback buffer, at which point
715 * we don't have the choice any more.
717 * Thanks to Jan Holmen Holsten for the idea and
718 * initial implementation.
720 if (disptop > -savelines && disptop < 0)
723 line = resizeline(line, cols);
724 for (i = 0; i < cols; i++)
725 line[i + 1] = erase_char;
727 addpos234(screen, line, botline);
730 * If the selection endpoints move into the scrollback,
731 * we keep them moving until they hit the top. However,
732 * of course, if the line _hasn't_ moved into the
733 * scrollback then we don't do this, and cut them off
734 * at the top of the scroll region.
736 * This applies to selstart and selend (for an existing
737 * selection), and also selanchor (for one being
738 * selected as we speak).
740 seltop = sb ? -savelines : topline;
742 if (selstart.y >= seltop && selstart.y <= botline) {
744 if (selstart.y < seltop) {
749 if (selend.y >= seltop && selend.y <= botline) {
751 if (selend.y < seltop) {
756 if (selanchor.y >= seltop && selanchor.y <= botline) {
758 if (selanchor.y < seltop) {
759 selanchor.y = seltop;
770 * Move the cursor to a given position, clipping at boundaries. We
771 * may or may not want to clip at the scroll margin: marg_clip is 0
772 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
773 * even _being_ outside the margins.
775 static void move(int x, int y, int marg_clip)
782 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
784 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
798 * Save or restore the cursor and SGR mode.
800 static void save_cursor(int save)
804 save_attr = curr_attr;
807 save_wnext = wrapnext;
808 save_csattr = cset_attr[cset];
809 save_sco_acs = sco_acs;
812 /* Make sure the window hasn't shrunk since the save */
818 curr_attr = save_attr;
821 wrapnext = save_wnext;
823 * wrapnext might reset to False if the x position is no
824 * longer at the rightmost edge.
826 if (wrapnext && curs.x < cols-1)
828 cset_attr[cset] = save_csattr;
829 sco_acs = save_sco_acs;
832 erase_char = (' ' | ATTR_ASCII |
833 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
838 * Erase a large portion of the screen: the whole screen, or the
839 * whole line, or parts thereof.
841 static void erase_lots(int line_only, int from_begin, int to_end)
845 unsigned long *ldata;
867 check_selection(start, end);
869 /* Clear screen also forces a full window redraw, just in case. */
870 if (start.y == 0 && start.x == 0 && end.y == rows)
873 ldata = lineptr(start.y);
874 while (poslt(start, end)) {
875 if (start.x == cols && !erase_lattr)
876 ldata[start.x] &= ~LATTR_WRAPPED;
878 ldata[start.x] = erase_char;
879 if (incpos(start) && start.y < rows)
880 ldata = lineptr(start.y);
885 * Insert or delete characters within the current line. n is +ve if
886 * insertion is desired, and -ve for deletion.
888 static void insch(int n)
890 int dir = (n < 0 ? -1 : +1);
893 unsigned long *ldata;
895 n = (n < 0 ? -n : n);
896 if (n > cols - curs.x)
898 m = cols - curs.x - n;
900 cursplus.x = curs.x + n;
901 check_selection(curs, cursplus);
902 ldata = lineptr(curs.y);
904 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
906 ldata[curs.x + m++] = erase_char;
908 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
910 ldata[curs.x + n] = erase_char;
915 * Toggle terminal mode `mode' to state `state'. (`query' indicates
916 * whether the mode is a DEC private one or a normal one.)
918 static void toggle_mode(int mode, int query, int state)
924 case 1: /* application cursor keys */
925 app_cursor_keys = state;
927 case 2: /* VT52 mode */
930 blink_is_real = FALSE;
933 blink_is_real = cfg.blinktext;
936 case 3: /* 80/132 columns */
938 if (!cfg.no_remote_resize)
939 request_resize(state ? 132 : 80, rows);
942 case 5: /* reverse video */
944 * Toggle reverse video. If we receive an OFF within the
945 * visual bell timeout period after an ON, we trigger an
946 * effective visual bell, so that ESC[?5hESC[?5l will
947 * always be an actually _visible_ visual bell.
949 ticks = GETTICKCOUNT();
950 /* turn off a previous vbell to avoid inconsistencies */
951 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
953 if (rvideo && !state && /* we're turning it off... */
954 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
955 /* If there's no vbell timeout already, or this one lasts
956 * longer, replace vbell_timeout with ours. */
958 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
959 vbell_startpoint = rvbell_startpoint;
960 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
961 } else if (!rvideo && state) {
962 /* This is an ON, so we notice the time and save it. */
963 rvbell_startpoint = ticks;
966 seen_disp_event = TRUE;
970 case 6: /* DEC origin mode */
973 case 7: /* auto wrap */
976 case 8: /* auto key repeat */
979 case 10: /* set local edit mode */
980 term_editing = state;
981 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
983 case 25: /* enable/disable cursor */
984 compatibility2(OTHER, VT220);
986 seen_disp_event = TRUE;
988 case 47: /* alternate screen */
989 compatibility(OTHER);
991 swap_screen(cfg.no_alt_screen ? 0 : state);
994 case 1000: /* xterm mouse 1 */
995 xterm_mouse = state ? 1 : 0;
996 set_raw_mouse_mode(state);
998 case 1002: /* xterm mouse 2 */
999 xterm_mouse = state ? 2 : 0;
1000 set_raw_mouse_mode(state);
1004 case 4: /* set insert mode */
1005 compatibility(VT102);
1008 case 12: /* set echo mode */
1009 term_echoing = !state;
1010 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
1012 case 20: /* Return sends ... */
1013 cr_lf_return = state;
1015 case 34: /* Make cursor BIG */
1016 compatibility2(OTHER, VT220);
1017 big_cursor = !state;
1022 * Process an OSC sequence: set window title or icon name.
1024 static void do_osc(void)
1027 while (osc_strlen--)
1028 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
1030 osc_string[osc_strlen] = '\0';
1031 switch (esc_args[0]) {
1034 if (!cfg.no_remote_wintitle)
1035 set_icon(osc_string);
1036 if (esc_args[0] == 1)
1038 /* fall through: parameter 0 means set both */
1041 if (!cfg.no_remote_wintitle)
1042 set_title(osc_string);
1049 * ANSI printing routines.
1051 static void term_print_setup(void)
1053 bufchain_clear(&printer_buf);
1054 print_job = printer_start_job(cfg.printer);
1056 static void term_print_flush(void)
1061 while ((size = bufchain_size(&printer_buf)) > 5) {
1062 bufchain_prefix(&printer_buf, &data, &len);
1065 printer_job_data(print_job, data, len);
1066 bufchain_consume(&printer_buf, len);
1069 static void term_print_finish(void)
1075 if (!printing && !only_printing)
1076 return; /* we need do nothing */
1079 while ((size = bufchain_size(&printer_buf)) > 0) {
1080 bufchain_prefix(&printer_buf, &data, &len);
1082 if (c == '\033' || c == '\233') {
1083 bufchain_consume(&printer_buf, size);
1086 printer_job_data(print_job, &c, 1);
1087 bufchain_consume(&printer_buf, 1);
1090 printer_finish_job(print_job);
1092 printing = only_printing = FALSE;
1096 * Remove everything currently in `inbuf' and stick it up on the
1097 * in-memory display. There's a big state machine in here to
1098 * process escape sequences...
1103 unsigned char localbuf[256], *chars;
1108 chars = NULL; /* placate compiler warnings */
1109 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1113 bufchain_prefix(&inbuf, &ret, &nchars);
1114 if (nchars > sizeof(localbuf))
1115 nchars = sizeof(localbuf);
1116 memcpy(localbuf, ret, nchars);
1117 bufchain_consume(&inbuf, nchars);
1119 assert(chars != NULL);
1125 * Optionally log the session traffic to a file. Useful for
1126 * debugging and possibly also useful for actual logging.
1128 if (cfg.logtype == LGTYP_DEBUG)
1129 logtraffic((unsigned char) c, LGTYP_DEBUG);
1135 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1136 * be able to display 8-bit characters, but I'll let that go 'cause
1141 * If we're printing, add the character to the printer
1145 bufchain_add(&printer_buf, &c, 1);
1148 * If we're in print-only mode, we use a much simpler
1149 * state machine designed only to recognise the ESC[4i
1150 * termination sequence.
1152 if (only_printing) {
1155 else if (c == (unsigned char)'\233')
1157 else if (c == '[' && print_state == 1)
1159 else if (c == '4' && print_state == 2)
1161 else if (c == 'i' && print_state == 3)
1165 if (print_state == 4) {
1166 term_print_finish();
1172 /* First see about all those translations. */
1173 if (termstate == TOPLEVEL) {
1175 switch (utf_state) {
1178 /* UTF-8 must be stateless so we ignore iso2022. */
1179 if (unitab_ctrl[c] != 0xFF)
1181 else c = ((unsigned char)c) | ATTR_ASCII;
1183 } else if ((c & 0xe0) == 0xc0) {
1184 utf_size = utf_state = 1;
1185 utf_char = (c & 0x1f);
1186 } else if ((c & 0xf0) == 0xe0) {
1187 utf_size = utf_state = 2;
1188 utf_char = (c & 0x0f);
1189 } else if ((c & 0xf8) == 0xf0) {
1190 utf_size = utf_state = 3;
1191 utf_char = (c & 0x07);
1192 } else if ((c & 0xfc) == 0xf8) {
1193 utf_size = utf_state = 4;
1194 utf_char = (c & 0x03);
1195 } else if ((c & 0xfe) == 0xfc) {
1196 utf_size = utf_state = 5;
1197 utf_char = (c & 0x01);
1208 if ((c & 0xC0) != 0x80) {
1214 utf_char = (utf_char << 6) | (c & 0x3f);
1220 /* Is somebody trying to be evil! */
1222 (c < 0x800 && utf_size >= 2) ||
1223 (c < 0x10000 && utf_size >= 3) ||
1224 (c < 0x200000 && utf_size >= 4) ||
1225 (c < 0x4000000 && utf_size >= 5))
1228 /* Unicode line separator and paragraph separator are CR-LF */
1229 if (c == 0x2028 || c == 0x2029)
1232 /* High controls are probably a Baaad idea too. */
1236 /* The UTF-16 surrogates are not nice either. */
1237 /* The standard give the option of decoding these:
1238 * I don't want to! */
1239 if (c >= 0xD800 && c < 0xE000)
1242 /* ISO 10646 characters now limited to UTF-16 range. */
1246 /* This is currently a TagPhobic application.. */
1247 if (c >= 0xE0000 && c <= 0xE007F)
1250 /* U+FEFF is best seen as a null. */
1253 /* But U+FFFE is an error. */
1254 if (c == 0xFFFE || c == 0xFFFF)
1257 /* Oops this is a 16bit implementation */
1262 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1264 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1266 if (sco_acs == 2) c ^= 0x80;
1269 switch (cset_attr[cset]) {
1271 * Linedraw characters are different from 'ESC ( B'
1272 * only for a small range. For ones outside that
1273 * range, make sure we use the same font as well as
1274 * the same encoding.
1277 if (unitab_ctrl[c] != 0xFF)
1280 c = ((unsigned char) c) | ATTR_LINEDRW;
1284 /* If UK-ASCII, make the '#' a LineDraw Pound */
1286 c = '}' | ATTR_LINEDRW;
1289 /*FALLTHROUGH*/ case ATTR_ASCII:
1290 if (unitab_ctrl[c] != 0xFF)
1293 c = ((unsigned char) c) | ATTR_ASCII;
1296 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1302 /* How about C1 controls ? */
1303 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1304 has_compat(VT220)) {
1305 termstate = SEEN_ESC;
1307 c = '@' + (c & 0x1F);
1310 /* Or the GL control. */
1311 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1312 if (curs.x && !wrapnext)
1316 if (!cfg.no_dbackspace) /* destructive bksp might be disabled */
1317 *cpos = (' ' | curr_attr | ATTR_ASCII);
1319 /* Or normal C0 controls. */
1320 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1322 case '\005': /* terminal type query */
1323 /* Strictly speaking this is VT100 but a VT100 defaults to
1324 * no response. Other terminals respond at their option.
1326 * Don't put a CR in the default string as this tends to
1327 * upset some weird software.
1329 * An xterm returns "xterm" (5 characters)
1331 compatibility(ANSIMIN);
1333 char abuf[256], *s, *d;
1335 for (s = cfg.answerback, d = abuf; *s; s++) {
1337 if (*s >= 'a' && *s <= 'z')
1338 *d++ = (*s - ('a' - 1));
1339 else if ((*s >= '@' && *s <= '_') ||
1340 *s == '?' || (*s & 0x80))
1345 } else if (*s == '^') {
1350 lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
1355 struct beeptime *newbeep;
1356 unsigned long ticks;
1358 ticks = GETTICKCOUNT();
1360 if (!beep_overloaded) {
1361 newbeep = smalloc(sizeof(struct beeptime));
1362 newbeep->ticks = ticks;
1363 newbeep->next = NULL;
1367 beeptail->next = newbeep;
1373 * Throw out any beeps that happened more than
1377 beephead->ticks < ticks - cfg.bellovl_t) {
1378 struct beeptime *tmp = beephead;
1379 beephead = tmp->next;
1386 if (cfg.bellovl && beep_overloaded &&
1387 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1389 * If we're currently overloaded and the
1390 * last beep was more than s seconds ago,
1391 * leave overload mode.
1393 beep_overloaded = FALSE;
1394 } else if (cfg.bellovl && !beep_overloaded &&
1395 nbeeps >= cfg.bellovl_n) {
1397 * Now, if we have n or more beeps
1398 * remaining in the queue, go into overload
1401 beep_overloaded = TRUE;
1406 * Perform an actual beep if we're not overloaded.
1408 if (!cfg.bellovl || !beep_overloaded) {
1409 if (cfg.beep == BELL_VISUAL) {
1411 vbell_startpoint = ticks;
1420 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1421 else if (curs.x == 0 && curs.y > 0)
1422 curs.x = cols - 1, curs.y--;
1428 seen_disp_event = TRUE;
1431 compatibility(VT100);
1435 compatibility(VT100);
1440 termstate = VT52_ESC;
1442 compatibility(ANSIMIN);
1443 termstate = SEEN_ESC;
1451 seen_disp_event = TRUE;
1453 logtraffic((unsigned char) c, LGTYP_ASCII);
1456 if (has_compat(SCOANSI)) {
1458 erase_lots(FALSE, FALSE, TRUE);
1461 seen_disp_event = 1;
1465 compatibility(VT100);
1467 if (curs.y == marg_b)
1468 scroll(marg_t, marg_b, 1, TRUE);
1469 else if (curs.y < rows - 1)
1475 seen_disp_event = 1;
1477 logtraffic((unsigned char) c, LGTYP_ASCII);
1481 pos old_curs = curs;
1482 unsigned long *ldata = lineptr(curs.y);
1486 } while (curs.x < cols - 1 && !tabs[curs.x]);
1488 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1489 if (curs.x >= cols / 2)
1490 curs.x = cols / 2 - 1;
1497 check_selection(old_curs, curs);
1499 seen_disp_event = TRUE;
1503 switch (termstate) {
1505 /* Only graphic characters get this far, ctrls are stripped above */
1506 if (wrapnext && wrap) {
1507 cpos[1] |= LATTR_WRAPPED;
1508 if (curs.y == marg_b)
1509 scroll(marg_t, marg_b, 1, TRUE);
1510 else if (curs.y < rows - 1)
1518 if (selstate != NO_SELECTION) {
1519 pos cursplus = curs;
1521 check_selection(curs, cursplus);
1523 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1524 logtraffic((unsigned char) c, LGTYP_ASCII);
1526 extern int wcwidth(wchar_t ucs);
1531 width = wcwidth((wchar_t) c);
1534 *cpos++ = c | curr_attr;
1535 if (++curs.x == cols) {
1536 *cpos |= LATTR_WRAPPED;
1537 if (curs.y == marg_b)
1538 scroll(marg_t, marg_b, 1, TRUE);
1539 else if (curs.y < rows - 1)
1544 *cpos++ = UCSWIDE | curr_attr;
1547 *cpos++ = c | curr_attr;
1554 if (curs.x == cols) {
1558 if (wrap && vt52_mode) {
1559 cpos[1] |= LATTR_WRAPPED;
1560 if (curs.y == marg_b)
1561 scroll(marg_t, marg_b, 1, TRUE);
1562 else if (curs.y < rows - 1)
1569 seen_disp_event = 1;
1574 * This state is virtually identical to SEEN_ESC, with the
1575 * exception that we have an OSC sequence in the pipeline,
1576 * and _if_ we see a backslash, we process it.
1580 termstate = TOPLEVEL;
1583 /* else fall through */
1585 if (c >= ' ' && c <= '/') {
1592 termstate = TOPLEVEL;
1593 switch (ANSI(c, esc_query)) {
1594 case '[': /* enter CSI mode */
1595 termstate = SEEN_CSI;
1597 esc_args[0] = ARG_DEFAULT;
1600 case ']': /* xterm escape sequences */
1601 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1602 compatibility(OTHER);
1603 termstate = SEEN_OSC;
1606 case '7': /* save cursor */
1607 compatibility(VT100);
1610 case '8': /* restore cursor */
1611 compatibility(VT100);
1613 seen_disp_event = TRUE;
1616 compatibility(VT100);
1617 app_keypad_keys = TRUE;
1620 compatibility(VT100);
1621 app_keypad_keys = FALSE;
1623 case 'D': /* exactly equivalent to LF */
1624 compatibility(VT100);
1625 if (curs.y == marg_b)
1626 scroll(marg_t, marg_b, 1, TRUE);
1627 else if (curs.y < rows - 1)
1631 seen_disp_event = TRUE;
1633 case 'E': /* exactly equivalent to CR-LF */
1634 compatibility(VT100);
1636 if (curs.y == marg_b)
1637 scroll(marg_t, marg_b, 1, TRUE);
1638 else if (curs.y < rows - 1)
1642 seen_disp_event = TRUE;
1644 case 'M': /* reverse index - backwards LF */
1645 compatibility(VT100);
1646 if (curs.y == marg_t)
1647 scroll(marg_t, marg_b, -1, TRUE);
1648 else if (curs.y > 0)
1652 seen_disp_event = TRUE;
1654 case 'Z': /* terminal type query */
1655 compatibility(VT100);
1656 ldisc_send(id_string, strlen(id_string), 0);
1658 case 'c': /* restore power-on settings */
1659 compatibility(VT100);
1662 if (!cfg.no_remote_resize)
1663 request_resize(80, rows);
1668 seen_disp_event = TRUE;
1670 case 'H': /* set a tab */
1671 compatibility(VT100);
1672 tabs[curs.x] = TRUE;
1675 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1676 compatibility(VT100);
1678 unsigned long *ldata;
1682 for (i = 0; i < rows; i++) {
1684 for (j = 0; j < cols; j++)
1685 ldata[j] = ATTR_DEFAULT | 'E';
1689 seen_disp_event = TRUE;
1690 scrtop.x = scrtop.y = 0;
1693 check_selection(scrtop, scrbot);
1697 case ANSI('3', '#'):
1698 case ANSI('4', '#'):
1699 case ANSI('5', '#'):
1700 case ANSI('6', '#'):
1701 compatibility(VT100);
1703 unsigned long nlattr;
1704 unsigned long *ldata;
1705 switch (ANSI(c, esc_query)) {
1706 case ANSI('3', '#'):
1709 case ANSI('4', '#'):
1712 case ANSI('5', '#'):
1713 nlattr = LATTR_NORM;
1715 default: /* spiritually case ANSI('6', '#'): */
1716 nlattr = LATTR_WIDE;
1719 ldata = lineptr(curs.y);
1720 ldata[cols] &= ~LATTR_MODE;
1721 ldata[cols] |= nlattr;
1725 case ANSI('A', '('):
1726 compatibility(VT100);
1727 if (!cfg.no_remote_charset)
1728 cset_attr[0] = ATTR_GBCHR;
1730 case ANSI('B', '('):
1731 compatibility(VT100);
1732 if (!cfg.no_remote_charset)
1733 cset_attr[0] = ATTR_ASCII;
1735 case ANSI('0', '('):
1736 compatibility(VT100);
1737 if (!cfg.no_remote_charset)
1738 cset_attr[0] = ATTR_LINEDRW;
1740 case ANSI('U', '('):
1741 compatibility(OTHER);
1742 if (!cfg.no_remote_charset)
1743 cset_attr[0] = ATTR_SCOACS;
1746 case ANSI('A', ')'):
1747 compatibility(VT100);
1748 if (!cfg.no_remote_charset)
1749 cset_attr[1] = ATTR_GBCHR;
1751 case ANSI('B', ')'):
1752 compatibility(VT100);
1753 if (!cfg.no_remote_charset)
1754 cset_attr[1] = ATTR_ASCII;
1756 case ANSI('0', ')'):
1757 compatibility(VT100);
1758 if (!cfg.no_remote_charset)
1759 cset_attr[1] = ATTR_LINEDRW;
1761 case ANSI('U', ')'):
1762 compatibility(OTHER);
1763 if (!cfg.no_remote_charset)
1764 cset_attr[1] = ATTR_SCOACS;
1767 case ANSI('8', '%'): /* Old Linux code */
1768 case ANSI('G', '%'):
1769 compatibility(OTHER);
1770 if (!cfg.no_remote_charset)
1773 case ANSI('@', '%'):
1774 compatibility(OTHER);
1775 if (!cfg.no_remote_charset)
1781 termstate = TOPLEVEL; /* default */
1783 if (esc_nargs <= ARGS_MAX) {
1784 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1785 esc_args[esc_nargs - 1] = 0;
1786 esc_args[esc_nargs - 1] =
1787 10 * esc_args[esc_nargs - 1] + c - '0';
1789 termstate = SEEN_CSI;
1790 } else if (c == ';') {
1791 if (++esc_nargs <= ARGS_MAX)
1792 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1793 termstate = SEEN_CSI;
1794 } else if (c < '@') {
1801 termstate = SEEN_CSI;
1803 switch (ANSI(c, esc_query)) {
1804 case 'A': /* move up N lines */
1805 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1806 seen_disp_event = TRUE;
1808 case 'e': /* move down N lines */
1809 compatibility(ANSI);
1812 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1813 seen_disp_event = TRUE;
1815 case ANSI('c', '>'): /* report xterm version */
1816 compatibility(OTHER);
1817 /* this reports xterm version 136 so that VIM can
1818 use the drag messages from the mouse reporting */
1819 ldisc_send("\033[>0;136;0c", 11, 0);
1821 case 'a': /* move right N cols */
1822 compatibility(ANSI);
1825 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1826 seen_disp_event = TRUE;
1828 case 'D': /* move left N cols */
1829 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1830 seen_disp_event = TRUE;
1832 case 'E': /* move down N lines and CR */
1833 compatibility(ANSI);
1834 move(0, curs.y + def(esc_args[0], 1), 1);
1835 seen_disp_event = TRUE;
1837 case 'F': /* move up N lines and CR */
1838 compatibility(ANSI);
1839 move(0, curs.y - def(esc_args[0], 1), 1);
1840 seen_disp_event = TRUE;
1843 case '`': /* set horizontal posn */
1844 compatibility(ANSI);
1845 move(def(esc_args[0], 1) - 1, curs.y, 0);
1846 seen_disp_event = TRUE;
1848 case 'd': /* set vertical posn */
1849 compatibility(ANSI);
1851 (dec_om ? marg_t : 0) + def(esc_args[0],
1854 seen_disp_event = TRUE;
1857 case 'f': /* set horz and vert posns at once */
1859 esc_args[1] = ARG_DEFAULT;
1860 move(def(esc_args[1], 1) - 1,
1861 (dec_om ? marg_t : 0) + def(esc_args[0],
1864 seen_disp_event = TRUE;
1866 case 'J': /* erase screen or parts of it */
1868 unsigned int i = def(esc_args[0], 0) + 1;
1871 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1874 seen_disp_event = TRUE;
1876 case 'K': /* erase line or parts of it */
1878 unsigned int i = def(esc_args[0], 0) + 1;
1881 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1883 seen_disp_event = TRUE;
1885 case 'L': /* insert lines */
1886 compatibility(VT102);
1887 if (curs.y <= marg_b)
1888 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1891 seen_disp_event = TRUE;
1893 case 'M': /* delete lines */
1894 compatibility(VT102);
1895 if (curs.y <= marg_b)
1896 scroll(curs.y, marg_b, def(esc_args[0], 1),
1899 seen_disp_event = TRUE;
1901 case '@': /* insert chars */
1902 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1903 compatibility(VT102);
1904 insch(def(esc_args[0], 1));
1905 seen_disp_event = TRUE;
1907 case 'P': /* delete chars */
1908 compatibility(VT102);
1909 insch(-def(esc_args[0], 1));
1910 seen_disp_event = TRUE;
1912 case 'c': /* terminal type query */
1913 compatibility(VT100);
1914 /* This is the response for a VT102 */
1915 ldisc_send(id_string, strlen(id_string), 0);
1917 case 'n': /* cursor position query */
1918 if (esc_args[0] == 6) {
1920 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1922 ldisc_send(buf, strlen(buf), 0);
1923 } else if (esc_args[0] == 5) {
1924 ldisc_send("\033[0n", 4, 0);
1927 case 'h': /* toggle modes to high */
1929 compatibility(VT100);
1932 for (i = 0; i < esc_nargs; i++)
1933 toggle_mode(esc_args[i], esc_query, TRUE);
1938 compatibility(VT100);
1940 if (esc_nargs != 1) break;
1941 if (esc_args[0] == 5 && *cfg.printer) {
1943 only_printing = !esc_query;
1946 } else if (esc_args[0] == 4 && printing) {
1947 term_print_finish();
1951 case 'l': /* toggle modes to low */
1953 compatibility(VT100);
1956 for (i = 0; i < esc_nargs; i++)
1957 toggle_mode(esc_args[i], esc_query, FALSE);
1960 case 'g': /* clear tabs */
1961 compatibility(VT100);
1962 if (esc_nargs == 1) {
1963 if (esc_args[0] == 0) {
1964 tabs[curs.x] = FALSE;
1965 } else if (esc_args[0] == 3) {
1967 for (i = 0; i < cols; i++)
1972 case 'r': /* set scroll margins */
1973 compatibility(VT100);
1974 if (esc_nargs <= 2) {
1976 top = def(esc_args[0], 1) - 1;
1977 bot = (esc_nargs <= 1
1979 0 ? rows : def(esc_args[1], rows)) - 1;
1982 /* VTTEST Bug 9 - if region is less than 2 lines
1983 * don't change region.
1985 if (bot - top > 0) {
1990 * I used to think the cursor should be
1991 * placed at the top of the newly marginned
1992 * area. Apparently not: VMS TPU falls over
1995 * Well actually it should for Origin mode - RDB
1997 curs.y = (dec_om ? marg_t : 0);
1999 seen_disp_event = TRUE;
2003 case 'm': /* set graphics rendition */
2006 * A VT100 without the AVO only had one attribute, either
2007 * underline or reverse video depending on the cursor type,
2008 * this was selected by CSI 7m.
2011 * This is sometimes DIM, eg on the GIGI and Linux
2013 * This is sometimes INVIS various ANSI.
2015 * This like 22 disables BOLD, DIM and INVIS
2017 * The ANSI colours appear on any terminal that has colour
2018 * (obviously) but the interaction between sgr0 and the
2019 * colours varies but is usually related to the background
2020 * colour erase item.
2021 * The interaction between colour attributes and the mono
2022 * ones is also very implementation dependent.
2024 * The 39 and 49 attributes are likely to be unimplemented.
2027 for (i = 0; i < esc_nargs; i++) {
2028 switch (def(esc_args[i], 0)) {
2029 case 0: /* restore defaults */
2030 curr_attr = ATTR_DEFAULT;
2032 case 1: /* enable bold */
2033 compatibility(VT100AVO);
2034 curr_attr |= ATTR_BOLD;
2036 case 21: /* (enable double underline) */
2037 compatibility(OTHER);
2038 case 4: /* enable underline */
2039 compatibility(VT100AVO);
2040 curr_attr |= ATTR_UNDER;
2042 case 5: /* enable blink */
2043 compatibility(VT100AVO);
2044 curr_attr |= ATTR_BLINK;
2046 case 7: /* enable reverse video */
2047 curr_attr |= ATTR_REVERSE;
2049 case 10: /* SCO acs off */
2050 compatibility(SCOANSI);
2051 if (cfg.no_remote_charset) break;
2053 case 11: /* SCO acs on */
2054 compatibility(SCOANSI);
2055 if (cfg.no_remote_charset) break;
2057 case 12: /* SCO acs on flipped */
2058 compatibility(SCOANSI);
2059 if (cfg.no_remote_charset) break;
2061 case 22: /* disable bold */
2062 compatibility2(OTHER, VT220);
2063 curr_attr &= ~ATTR_BOLD;
2065 case 24: /* disable underline */
2066 compatibility2(OTHER, VT220);
2067 curr_attr &= ~ATTR_UNDER;
2069 case 25: /* disable blink */
2070 compatibility2(OTHER, VT220);
2071 curr_attr &= ~ATTR_BLINK;
2073 case 27: /* disable reverse video */
2074 compatibility2(OTHER, VT220);
2075 curr_attr &= ~ATTR_REVERSE;
2086 curr_attr &= ~ATTR_FGMASK;
2088 (esc_args[i] - 30) << ATTR_FGSHIFT;
2090 case 39: /* default-foreground */
2091 curr_attr &= ~ATTR_FGMASK;
2092 curr_attr |= ATTR_DEFFG;
2103 curr_attr &= ~ATTR_BGMASK;
2105 (esc_args[i] - 40) << ATTR_BGSHIFT;
2107 case 49: /* default-background */
2108 curr_attr &= ~ATTR_BGMASK;
2109 curr_attr |= ATTR_DEFBG;
2114 erase_char = (' ' | ATTR_ASCII |
2116 (ATTR_FGMASK | ATTR_BGMASK)));
2119 case 's': /* save cursor */
2122 case 'u': /* restore cursor */
2124 seen_disp_event = TRUE;
2126 case 't': /* set page size - ie window height */
2128 * VT340/VT420 sequence DECSLPP, DEC only allows values
2129 * 24/25/36/48/72/144 other emulators (eg dtterm) use
2130 * illegal values (eg first arg 1..9) for window changing
2134 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
2135 compatibility(VT340TEXT);
2136 if (!cfg.no_remote_resize)
2137 request_resize(cols, def(esc_args[0], 24));
2139 } else if (esc_nargs >= 1 &&
2142 compatibility(OTHER);
2144 switch (esc_args[0]) {
2154 if (esc_nargs >= 3) {
2155 if (!cfg.no_remote_resize)
2156 move_window(def(esc_args[1], 0),
2157 def(esc_args[2], 0));
2161 /* We should resize the window to a given
2162 * size in pixels here, but currently our
2163 * resizing code isn't healthy enough to
2167 set_zorder(TRUE); /* move to top */
2170 set_zorder(FALSE); /* move to bottom */
2176 if (esc_nargs >= 3) {
2177 if (!cfg.no_remote_resize)
2178 request_resize(def(esc_args[2], cfg.width),
2179 def(esc_args[1], cfg.height));
2184 set_zoomed(esc_args[1] ? TRUE : FALSE);
2187 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2191 get_window_pos(&x, &y);
2192 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2193 ldisc_send(buf, len, 0);
2196 get_window_pixels(&x, &y);
2197 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2198 ldisc_send(buf, len, 0);
2201 len = sprintf(buf, "\033[8;%d;%dt",
2203 ldisc_send(buf, len, 0);
2207 * Hmmm. Strictly speaking we
2208 * should return `the size of the
2209 * screen in characters', but
2210 * that's not easy: (a) window
2211 * furniture being what it is it's
2212 * hard to compute, and (b) in
2213 * resize-font mode maximising the
2214 * window wouldn't change the
2215 * number of characters. *shrug*. I
2216 * think we'll ignore it for the
2217 * moment and see if anyone
2218 * complains, and then ask them
2219 * what they would like it to do.
2223 p = get_window_title(TRUE);
2225 ldisc_send("\033]L", 3, 0);
2226 ldisc_send(p, len, 0);
2227 ldisc_send("\033\\", 2, 0);
2230 p = get_window_title(FALSE);
2232 ldisc_send("\033]l", 3, 0);
2233 ldisc_send(p, len, 0);
2234 ldisc_send("\033\\", 2, 0);
2240 compatibility(SCOANSI);
2241 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2244 seen_disp_event = TRUE;
2247 compatibility(SCOANSI);
2248 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2251 seen_disp_event = TRUE;
2253 case ANSI('|', '*'):
2254 /* VT420 sequence DECSNLS
2255 * Set number of lines on screen
2256 * VT420 uses VGA like hardware and can support any size in
2257 * reasonable range (24..49 AIUI) with no default specified.
2259 compatibility(VT420);
2260 if (esc_nargs == 1 && esc_args[0] > 0) {
2261 if (!cfg.no_remote_resize)
2262 request_resize(cols, def(esc_args[0], cfg.height));
2266 case ANSI('|', '$'):
2267 /* VT340/VT420 sequence DECSCPP
2268 * Set number of columns per page
2269 * Docs imply range is only 80 or 132, but I'll allow any.
2271 compatibility(VT340TEXT);
2272 if (esc_nargs <= 1) {
2273 if (!cfg.no_remote_resize)
2274 request_resize(def(esc_args[0], cfg.width), rows);
2278 case 'X': /* write N spaces w/o moving cursor */
2279 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2280 compatibility(ANSIMIN);
2282 int n = def(esc_args[0], 1);
2284 unsigned long *p = cpos;
2285 if (n > cols - curs.x)
2289 check_selection(curs, cursplus);
2292 seen_disp_event = TRUE;
2295 case 'x': /* report terminal characteristics */
2296 compatibility(VT100);
2299 int i = def(esc_args[0], 0);
2300 if (i == 0 || i == 1) {
2301 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2303 ldisc_send(buf, 20, 0);
2307 case 'Z': /* BackTab for xterm */
2308 compatibility(OTHER);
2310 int i = def(esc_args[0], 1);
2311 pos old_curs = curs;
2313 for(;i>0 && curs.x>0; i--) {
2316 } while (curs.x >0 && !tabs[curs.x]);
2319 check_selection(old_curs, curs);
2322 case ANSI('L', '='):
2323 compatibility(OTHER);
2324 use_bce = (esc_args[0] <= 0);
2325 erase_char = ERASE_CHAR;
2327 erase_char = (' ' | ATTR_ASCII |
2329 (ATTR_FGMASK | ATTR_BGMASK)));
2331 case ANSI('E', '='):
2332 compatibility(OTHER);
2333 blink_is_real = (esc_args[0] >= 1);
2335 case ANSI('p', '"'):
2336 /* Allow the host to make this emulator a 'perfect' VT102.
2337 * This first appeared in the VT220, but we do need to get
2338 * back to PuTTY mode so I won't check it.
2340 * The arg in 40..42,50 are a PuTTY extension.
2341 * The 2nd arg, 8bit vs 7bit is not checked.
2343 * Setting VT102 mode should also change the Fkeys to
2344 * generate PF* codes as a real VT102 has no Fkeys.
2345 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2348 * Note ESC c will NOT change this!
2351 switch (esc_args[0]) {
2353 compatibility_level &= ~TM_VTXXX;
2354 compatibility_level |= TM_VT102;
2357 compatibility_level &= ~TM_VTXXX;
2358 compatibility_level |= TM_VT220;
2362 if (esc_args[0] > 60 && esc_args[0] < 70)
2363 compatibility_level |= TM_VTXXX;
2367 compatibility_level &= TM_VTXXX;
2370 compatibility_level = TM_PUTTY;
2373 compatibility_level = TM_SCOANSI;
2377 compatibility_level = TM_PUTTY;
2383 /* Change the response to CSI c */
2384 if (esc_args[0] == 50) {
2387 strcpy(id_string, "\033[?");
2388 for (i = 1; i < esc_nargs; i++) {
2390 strcat(id_string, ";");
2391 sprintf(lbuf, "%d", esc_args[i]);
2392 strcat(id_string, lbuf);
2394 strcat(id_string, "c");
2397 /* Is this a good idea ?
2398 * Well we should do a soft reset at this point ...
2400 if (!has_compat(VT420) && has_compat(VT100)) {
2401 if (!cfg.no_remote_resize) {
2403 request_resize(132, 24);
2405 request_resize(80, 24);
2415 case 'P': /* Linux palette sequence */
2416 termstate = SEEN_OSC_P;
2419 case 'R': /* Linux palette reset */
2422 termstate = TOPLEVEL;
2424 case 'W': /* word-set */
2425 termstate = SEEN_OSC_W;
2438 esc_args[0] = 10 * esc_args[0] + c - '0';
2442 * Grotty hack to support xterm and DECterm title
2443 * sequences concurrently.
2445 if (esc_args[0] == 2) {
2449 /* else fall through */
2451 termstate = OSC_STRING;
2457 * This OSC stuff is EVIL. It takes just one character to get into
2458 * sysline mode and it's not initially obvious how to get out.
2459 * So I've added CR and LF as string aborts.
2460 * This shouldn't effect compatibility as I believe embedded
2461 * control characters are supposed to be interpreted (maybe?)
2462 * and they don't display anything useful anyway.
2466 if (c == '\n' || c == '\r') {
2467 termstate = TOPLEVEL;
2468 } else if (c == 0234 || c == '\007') {
2470 * These characters terminate the string; ST and BEL
2471 * terminate the sequence and trigger instant
2472 * processing of it, whereas ESC goes back to SEEN_ESC
2473 * mode unless it is followed by \, in which case it is
2474 * synonymous with ST in the first place.
2477 termstate = TOPLEVEL;
2478 } else if (c == '\033')
2479 termstate = OSC_MAYBE_ST;
2480 else if (osc_strlen < OSC_STR_MAX)
2481 osc_string[osc_strlen++] = c;
2485 int max = (osc_strlen == 0 ? 21 : 16);
2487 if (c >= '0' && c <= '9')
2489 else if (c >= 'A' && c <= 'A' + max - 10)
2491 else if (c >= 'a' && c <= 'a' + max - 10)
2494 termstate = TOPLEVEL;
2497 osc_string[osc_strlen++] = val;
2498 if (osc_strlen >= 7) {
2499 palette_set(osc_string[0],
2500 osc_string[1] * 16 + osc_string[2],
2501 osc_string[3] * 16 + osc_string[4],
2502 osc_string[5] * 16 + osc_string[6]);
2504 termstate = TOPLEVEL;
2520 esc_args[0] = 10 * esc_args[0] + c - '0';
2523 termstate = OSC_STRING;
2528 termstate = TOPLEVEL;
2529 seen_disp_event = TRUE;
2532 move(curs.x, curs.y - 1, 1);
2535 move(curs.x, curs.y + 1, 1);
2538 move(curs.x + 1, curs.y, 1);
2541 move(curs.x - 1, curs.y, 1);
2544 * From the VT100 Manual
2545 * NOTE: The special graphics characters in the VT100
2546 * are different from those in the VT52
2548 * From VT102 manual:
2549 * 137 _ Blank - Same
2550 * 140 ` Reserved - Humm.
2551 * 141 a Solid rectangle - Similar
2552 * 142 b 1/ - Top half of fraction for the
2553 * 143 c 3/ - subscript numbers below.
2556 * 146 f Degrees - Same
2557 * 147 g Plus or minus - Same
2559 * 151 i Ellipsis (dots)
2562 * 154 l Bar at scan 0
2563 * 155 m Bar at scan 1
2564 * 156 n Bar at scan 2
2565 * 157 o Bar at scan 3 - Similar
2566 * 160 p Bar at scan 4 - Similar
2567 * 161 q Bar at scan 5 - Similar
2568 * 162 r Bar at scan 6 - Same
2569 * 163 s Bar at scan 7 - Similar
2584 cset_attr[cset = 0] = ATTR_LINEDRW;
2587 cset_attr[cset = 0] = ATTR_ASCII;
2594 scroll(0, rows - 1, -1, TRUE);
2595 else if (curs.y > 0)
2601 erase_lots(FALSE, FALSE, TRUE);
2605 erase_lots(TRUE, FALSE, TRUE);
2609 /* XXX Print cursor line */
2612 /* XXX Start controller mode */
2615 /* XXX Stop controller mode */
2619 termstate = VT52_Y1;
2622 ldisc_send("\033/Z", 3, 0);
2625 app_keypad_keys = TRUE;
2628 app_keypad_keys = FALSE;
2631 /* XXX This should switch to VT100 mode not current or default
2632 * VT mode. But this will only have effect in a VT220+
2636 blink_is_real = cfg.blinktext;
2640 /* XXX Enter auto print mode */
2643 /* XXX Exit auto print mode */
2646 /* XXX Print screen */
2652 /* compatibility(ATARI) */
2654 erase_lots(FALSE, FALSE, TRUE);
2658 /* compatibility(ATARI) */
2659 if (curs.y <= marg_b)
2660 scroll(curs.y, marg_b, -1, FALSE);
2663 /* compatibility(ATARI) */
2664 if (curs.y <= marg_b)
2665 scroll(curs.y, marg_b, 1, TRUE);
2668 /* compatibility(ATARI) */
2669 termstate = VT52_FG;
2672 /* compatibility(ATARI) */
2673 termstate = VT52_BG;
2676 /* compatibility(ATARI) */
2677 erase_lots(FALSE, TRUE, FALSE);
2681 /* compatibility(ATARI) */
2685 /* compatibility(ATARI) */
2688 /* case 'j': Save cursor position - broken on ST */
2689 /* case 'k': Restore cursor position */
2691 /* compatibility(ATARI) */
2692 erase_lots(TRUE, TRUE, TRUE);
2698 /* compatibility(ATARI) */
2699 erase_lots(TRUE, TRUE, FALSE);
2702 /* compatibility(ATARI) */
2703 curr_attr |= ATTR_REVERSE;
2706 /* compatibility(ATARI) */
2707 curr_attr &= ~ATTR_REVERSE;
2709 case 'v': /* wrap Autowrap on - Wyse style */
2710 /* compatibility(ATARI) */
2713 case 'w': /* Autowrap off */
2714 /* compatibility(ATARI) */
2719 /* compatibility(OTHER) */
2721 curr_attr = ATTR_DEFAULT;
2723 erase_char = (' ' | ATTR_ASCII |
2725 (ATTR_FGMASK | ATTR_BGMASK)));
2728 /* compatibility(VI50) */
2729 curr_attr |= ATTR_UNDER;
2732 /* compatibility(VI50) */
2733 curr_attr &= ~ATTR_UNDER;
2736 /* compatibility(VI50) */
2738 curr_attr |= ATTR_BOLD;
2741 /* compatibility(VI50) */
2743 curr_attr &= ~ATTR_BOLD;
2749 termstate = VT52_Y2;
2750 move(curs.x, c - ' ', 0);
2753 termstate = TOPLEVEL;
2754 move(c - ' ', curs.y, 0);
2759 termstate = TOPLEVEL;
2760 curr_attr &= ~ATTR_FGMASK;
2761 curr_attr &= ~ATTR_BOLD;
2762 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2763 if ((c & 0x8) || vt52_bold)
2764 curr_attr |= ATTR_BOLD;
2767 erase_char = (' ' | ATTR_ASCII |
2768 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2771 termstate = TOPLEVEL;
2772 curr_attr &= ~ATTR_BGMASK;
2773 curr_attr &= ~ATTR_BLINK;
2774 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2776 /* Note: bold background */
2778 curr_attr |= ATTR_BLINK;
2781 erase_char = (' ' | ATTR_ASCII |
2782 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2785 default: break; /* placate gcc warning about enum use */
2787 if (selstate != NO_SELECTION) {
2788 pos cursplus = curs;
2790 check_selection(curs, cursplus);
2799 * Compare two lines to determine whether they are sufficiently
2800 * alike to scroll-optimise one to the other. Return the degree of
2803 static int linecmp(unsigned long *a, unsigned long *b)
2807 for (i = n = 0; i < cols; i++)
2808 n += (*a++ == *b++);
2814 * Given a context, update the window. Out of paranoia, we don't
2815 * allow WM_PAINT responses to do scrolling optimisations.
2817 static void do_paint(Context ctx, int may_optimise)
2819 int i, j, our_curs_y;
2820 unsigned long rv, cursor;
2823 long cursor_background = ERASE_CHAR;
2824 unsigned long ticks;
2827 * Check the visual bell state.
2830 ticks = GETTICKCOUNT();
2831 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2835 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2838 * screen array, disptop, scrtop,
2840 * cfg.blinkpc, blink_is_real, tblinker,
2841 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2844 /* Has the cursor position or type changed ? */
2847 if (blinker || !cfg.blink_cur)
2848 cursor = TATTR_ACTCURS;
2852 cursor = TATTR_PASCURS;
2854 cursor |= TATTR_RIGHTCURS;
2857 our_curs_y = curs.y - disptop;
2859 if (dispcurs && (curstype != cursor ||
2861 disptext + our_curs_y * (cols + 1) + curs.x)) {
2862 if (dispcurs > disptext &&
2863 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2864 dispcurs[-1] |= ATTR_INVALID;
2865 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2866 dispcurs[1] |= ATTR_INVALID;
2867 *dispcurs |= ATTR_INVALID;
2872 /* The normal screen data */
2873 for (i = 0; i < rows; i++) {
2874 unsigned long *ldata;
2876 int idx, dirty_line, dirty_run, selected;
2877 unsigned long attr = 0;
2878 int updated_line = 0;
2881 int last_run_dirty = 0;
2883 scrpos.y = i + disptop;
2884 ldata = lineptr(scrpos.y);
2885 lattr = (ldata[cols] & LATTR_MODE);
2887 idx = i * (cols + 1);
2888 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2889 disptext[idx + cols] = ldata[cols];
2891 for (j = 0; j < cols; j++, idx++) {
2892 unsigned long tattr, tchar;
2893 unsigned long *d = ldata + j;
2897 tchar = (*d & (CHAR_MASK | CSET_MASK));
2898 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2899 switch (tchar & CSET_MASK) {
2901 tchar = unitab_line[tchar & 0xFF];
2904 tchar = unitab_xterm[tchar & 0xFF];
2907 tchar = unitab_scoacs[tchar&0xFF];
2910 tattr |= (tchar & CSET_MASK);
2912 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2915 /* Video reversing things */
2916 if (seltype == LEXICOGRAPHIC)
2917 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2919 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2921 ^ (selected ? ATTR_REVERSE : 0));
2923 /* 'Real' blinking ? */
2924 if (blink_is_real && (tattr & ATTR_BLINK)) {
2925 if (has_focus && tblinker) {
2927 tattr &= ~CSET_MASK;
2930 tattr &= ~ATTR_BLINK;
2934 * Check the font we'll _probably_ be using to see if
2935 * the character is wide when we don't want it to be.
2937 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2938 if ((tattr & ATTR_WIDE) == 0 &&
2939 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2940 tattr |= ATTR_NARROW;
2941 } else if (disptext[idx]&ATTR_NARROW)
2942 tattr |= ATTR_NARROW;
2944 /* Cursor here ? Save the 'background' */
2945 if (i == our_curs_y && j == curs.x) {
2946 cursor_background = tattr | tchar;
2947 dispcurs = disptext + idx;
2950 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2953 break_run = (tattr != attr || j - start >= sizeof(ch));
2955 /* Special hack for VT100 Linedraw glyphs */
2956 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2957 && tchar <= 0xBD) break_run = TRUE;
2959 if (!dbcs_screenfont && !dirty_line) {
2960 if ((tchar | tattr) == disptext[idx])
2962 else if (!dirty_run && ccount == 1)
2967 if ((dirty_run || last_run_dirty) && ccount > 0) {
2968 do_text(ctx, start, i, ch, ccount, attr, lattr);
2974 if (dbcs_screenfont)
2975 last_run_dirty = dirty_run;
2976 dirty_run = dirty_line;
2979 if ((tchar | tattr) != disptext[idx])
2981 ch[ccount++] = (char) tchar;
2982 disptext[idx] = tchar | tattr;
2984 /* If it's a wide char step along to the next one. */
2985 if (tattr & ATTR_WIDE) {
2989 /* Cursor is here ? Ouch! */
2990 if (i == our_curs_y && j == curs.x) {
2991 cursor_background = *d;
2992 dispcurs = disptext + idx;
2994 if (disptext[idx] != *d)
3000 if (dirty_run && ccount > 0) {
3001 do_text(ctx, start, i, ch, ccount, attr, lattr);
3005 /* Cursor on this line ? (and changed) */
3006 if (i == our_curs_y && (curstype != cursor || updated_line)) {
3007 ch[0] = (char) (cursor_background & CHAR_MASK);
3008 attr = (cursor_background & ATTR_MASK) | cursor;
3009 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
3016 * Flick the switch that says if blinking things should be shown or hidden.
3019 void term_blink(int flg)
3021 static long last_blink = 0;
3022 static long last_tblink = 0;
3023 long now, blink_diff;
3025 now = GETTICKCOUNT();
3026 blink_diff = now - last_tblink;
3028 /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
3029 if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
3031 tblinker = !tblinker;
3040 blink_diff = now - last_blink;
3042 /* Make sure the cursor blinks no faster than system blink rate */
3043 if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
3051 * Invalidate the whole screen so it will be repainted in full.
3053 void term_invalidate(void)
3057 for (i = 0; i < rows * (cols + 1); i++)
3058 disptext[i] = ATTR_INVALID;
3062 * Paint the window in response to a WM_PAINT message.
3064 void term_paint(Context ctx, int left, int top, int right, int bottom)
3067 if (left < 0) left = 0;
3068 if (top < 0) top = 0;
3069 if (right >= cols) right = cols-1;
3070 if (bottom >= rows) bottom = rows-1;
3072 for (i = top; i <= bottom && i < rows; i++) {
3073 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
3074 for (j = left; j <= right && j < cols; j++)
3075 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3077 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
3078 disptext[i * (cols + 1) + j] = ATTR_INVALID;
3081 /* This should happen soon enough, also for some reason it sometimes
3082 * fails to actually do anything when re-sizing ... painting the wrong
3086 do_paint (ctx, FALSE);
3090 * Attempt to scroll the scrollback. The second parameter gives the
3091 * position we want to scroll to; the first is +1 to denote that
3092 * this position is relative to the beginning of the scrollback, -1
3093 * to denote it is relative to the end, and 0 to denote that it is
3094 * relative to the current position.
3096 void term_scroll(int rel, int where)
3098 int sbtop = -count234(scrollback);
3100 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
3101 if (disptop < sbtop)
3109 static void clipme(pos top, pos bottom, int rect)
3112 wchar_t *wbptr; /* where next char goes within workbuf */
3114 int wblen = 0; /* workbuf len */
3115 int buflen; /* amount of memory allocated to workbuf */
3117 buflen = 5120; /* Default size */
3118 workbuf = smalloc(buflen * sizeof(wchar_t));
3119 wbptr = workbuf; /* start filling here */
3120 old_top_x = top.x; /* needed for rect==1 */
3122 while (poslt(top, bottom)) {
3124 unsigned long *ldata = lineptr(top.y);
3128 * nlpos will point at the maximum position on this line we
3129 * should copy up to. So we start it at the end of the
3136 * ... move it backwards if there's unused space at the end
3137 * of the line (and also set `nl' if this is the case,
3138 * because in normal selection mode this means we need a
3139 * newline at the end)...
3141 if (!(ldata[cols] & LATTR_WRAPPED)) {
3142 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
3143 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
3144 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
3145 && poslt(top, nlpos))
3147 if (poslt(nlpos, bottom))
3152 * ... and then clip it to the terminal x coordinate if
3153 * we're doing rectangular selection. (In this case we
3154 * still did the above, so that copying e.g. the right-hand
3155 * column from a table doesn't fill with spaces on the
3159 if (nlpos.x > bottom.x)
3161 nl = (top.y < bottom.y);
3164 while (poslt(top, bottom) && poslt(top, nlpos)) {
3167 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
3169 wchar_t cbuf[16], *p;
3170 int uc = (ldata[top.x] & 0xFFFF);
3173 if (uc == UCSWIDE) {
3178 switch (uc & CSET_MASK) {
3181 uc = unitab_xterm[uc & 0xFF];
3185 uc = unitab_line[uc & 0xFF];
3188 uc = unitab_scoacs[uc&0xFF];
3191 switch (uc & CSET_MASK) {
3193 uc = unitab_font[uc & 0xFF];
3196 uc = unitab_oemcp[uc & 0xFF];
3200 set = (uc & CSET_MASK);
3201 c = (uc & CHAR_MASK);
3205 if (DIRECT_FONT(uc)) {
3206 if (c >= ' ' && c != 0x7F) {
3207 unsigned char buf[4];
3210 if (is_dbcs_leadbyte(font_codepage, (BYTE) c)) {
3212 buf[1] = (unsigned char) ldata[top.x + 1];
3213 rv = mb_to_wc(font_codepage, 0, buf, 2, wbuf, 4);
3217 rv = mb_to_wc(font_codepage, 0, buf, 1, wbuf, 4);
3221 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3228 for (p = cbuf; *p; p++) {
3229 /* Enough overhead for trailing NL and nul */
3230 if (wblen >= buflen - 16) {
3233 sizeof(wchar_t) * (buflen += 100));
3234 wbptr = workbuf + wblen;
3243 for (i = 0; i < sel_nl_sz; i++) {
3245 *wbptr++ = sel_nl[i];
3249 top.x = rect ? old_top_x : 0;
3251 #if SELECTION_NUL_TERMINATED
3255 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3256 if (buflen > 0) /* indicates we allocated this buffer */
3260 void term_copyall(void)
3263 top.y = -count234(scrollback);
3265 clipme(top, curs, 0);
3269 * The wordness array is mainly for deciding the disposition of the US-ASCII
3272 static int wordtype(int uc)
3275 int start, end, ctype;
3276 } *wptr, ucs_words[] = {
3282 0x037e, 0x037e, 1}, /* Greek question mark */
3284 0x0387, 0x0387, 1}, /* Greek ano teleia */
3286 0x055a, 0x055f, 1}, /* Armenian punctuation */
3288 0x0589, 0x0589, 1}, /* Armenian full stop */
3290 0x0700, 0x070d, 1}, /* Syriac punctuation */
3292 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3294 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3296 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3298 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3300 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3302 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3304 0x2000, 0x200a, 0}, /* Various spaces */
3306 0x2070, 0x207f, 2}, /* superscript */
3308 0x2080, 0x208f, 2}, /* subscript */
3310 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3312 0x3000, 0x3000, 0}, /* ideographic space */
3314 0x3001, 0x3020, 1}, /* ideographic punctuation */
3316 0x303f, 0x309f, 3}, /* Hiragana */
3318 0x30a0, 0x30ff, 3}, /* Katakana */
3320 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3322 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3324 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3326 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3328 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3330 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3332 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3334 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3336 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3341 uc &= (CSET_MASK | CHAR_MASK);
3343 switch (uc & CSET_MASK) {
3345 uc = unitab_xterm[uc & 0xFF];
3348 uc = unitab_line[uc & 0xFF];
3351 uc = unitab_scoacs[uc&0xFF];
3354 switch (uc & CSET_MASK) {
3356 uc = unitab_font[uc & 0xFF];
3359 uc = unitab_oemcp[uc & 0xFF];
3363 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3364 * fail as there's such a thing as a double width space. :-(
3366 if (dbcs_screenfont && font_codepage == line_codepage)
3370 return wordness[uc];
3372 for (wptr = ucs_words; wptr->start; wptr++) {
3373 if (uc >= wptr->start && uc <= wptr->end)
3381 * Spread the selection outwards according to the selection mode.
3383 static pos sel_spread_half(pos p, int dir)
3385 unsigned long *ldata;
3387 int topy = -count234(scrollback);
3389 ldata = lineptr(p.y);
3394 * In this mode, every character is a separate unit, except
3395 * for runs of spaces at the end of a non-wrapping line.
3397 if (!(ldata[cols] & LATTR_WRAPPED)) {
3398 unsigned long *q = ldata + cols;
3399 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3401 if (q == ldata + cols)
3403 if (p.x >= q - ldata)
3404 p.x = (dir == -1 ? q - ldata : cols - 1);
3409 * In this mode, the units are maximal runs of characters
3410 * whose `wordness' has the same value.
3412 wvalue = wordtype(ldata[p.x]);
3416 if (wordtype(ldata[p.x + 1]) == wvalue)
3421 if (ldata[cols] & LATTR_WRAPPED) {
3422 unsigned long *ldata2;
3423 ldata2 = lineptr(p.y+1);
3424 if (wordtype(ldata2[0]) == wvalue) {
3437 if (wordtype(ldata[p.x - 1]) == wvalue)
3442 unsigned long *ldata2;
3445 ldata2 = lineptr(p.y-1);
3446 if ((ldata2[cols] & LATTR_WRAPPED) &&
3447 wordtype(ldata2[cols-1]) == wvalue) {
3459 * In this mode, every line is a unit.
3461 p.x = (dir == -1 ? 0 : cols - 1);
3467 static void sel_spread(void)
3469 if (seltype == LEXICOGRAPHIC) {
3470 selstart = sel_spread_half(selstart, -1);
3472 selend = sel_spread_half(selend, +1);
3477 void term_do_paste(void)
3482 get_clip(&data, &len);
3483 if (data && len > 0) {
3486 term_seen_key_event(); /* pasted data counts */
3489 sfree(paste_buffer);
3490 paste_pos = paste_hold = paste_len = 0;
3491 paste_buffer = smalloc(len * sizeof(wchar_t));
3494 while (p < data + len) {
3495 while (p < data + len &&
3496 !(p <= data + len - sel_nl_sz &&
3497 !memcmp(p, sel_nl, sizeof(sel_nl))))
3502 for (i = 0; i < p - q; i++) {
3503 paste_buffer[paste_len++] = q[i];
3507 if (p <= data + len - sel_nl_sz &&
3508 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3509 paste_buffer[paste_len++] = '\r';
3515 /* Assume a small paste will be OK in one go. */
3516 if (paste_len < 256) {
3517 luni_send(paste_buffer, paste_len, 0);
3519 sfree(paste_buffer);
3521 paste_pos = paste_hold = paste_len = 0;
3524 get_clip(NULL, NULL);
3527 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3528 int shift, int ctrl, int alt)
3531 unsigned long *ldata;
3532 int raw_mouse = (xterm_mouse &&
3533 !cfg.no_mouse_rep &&
3534 !(cfg.mouse_override && shift));
3535 int default_seltype;
3539 if (a == MA_DRAG && !raw_mouse)
3544 if (a == MA_DRAG && !raw_mouse)
3557 selpoint.y = y + disptop;
3559 ldata = lineptr(selpoint.y);
3560 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3564 int encstate = 0, r, c;
3566 static int is_down = 0;
3570 encstate = 0x20; /* left button down */
3581 case MBT_WHEEL_DOWN:
3584 default: break; /* placate gcc warning about enum use */
3588 if (xterm_mouse == 1)
3601 default: break; /* placate gcc warning about enum use */
3610 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3611 ldisc_send(abuf, 6, 0);
3615 b = translate_button(b);
3618 * Set the selection type (rectangular or normal) at the start
3619 * of a selection attempt, from the state of Alt.
3621 if (!alt ^ !cfg.rect_select)
3622 default_seltype = RECTANGULAR;
3624 default_seltype = LEXICOGRAPHIC;
3626 if (selstate == NO_SELECTION) {
3627 seltype = default_seltype;
3630 if (b == MBT_SELECT && a == MA_CLICK) {
3632 selstate = ABOUT_TO;
3633 seltype = default_seltype;
3634 selanchor = selpoint;
3636 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3638 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3639 selstate = DRAGGING;
3640 selstart = selanchor = selpoint;
3644 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3645 (b == MBT_EXTEND && a != MA_RELEASE)) {
3646 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3648 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3649 if (seltype == LEXICOGRAPHIC) {
3651 * For normal selection, we extend by moving
3652 * whichever end of the current selection is closer
3655 if (posdiff(selpoint, selstart) <
3656 posdiff(selend, selstart) / 2) {
3660 selanchor = selstart;
3664 * For rectangular selection, we have a choice of
3665 * _four_ places to put selanchor and selpoint: the
3666 * four corners of the selection.
3668 if (2*selpoint.x < selstart.x + selend.x)
3669 selanchor.x = selend.x-1;
3671 selanchor.x = selstart.x;
3673 if (2*selpoint.y < selstart.y + selend.y)
3674 selanchor.y = selend.y;
3676 selanchor.y = selstart.y;
3678 selstate = DRAGGING;
3680 if (selstate != ABOUT_TO && selstate != DRAGGING)
3681 selanchor = selpoint;
3682 selstate = DRAGGING;
3683 if (seltype == LEXICOGRAPHIC) {
3685 * For normal selection, we set (selstart,selend) to
3686 * (selpoint,selanchor) in some order.
3688 if (poslt(selpoint, selanchor)) {
3689 selstart = selpoint;
3693 selstart = selanchor;
3699 * For rectangular selection, we may need to
3700 * interchange x and y coordinates (if the user has
3701 * dragged in the -x and +y directions, or vice versa).
3703 selstart.x = min(selanchor.x, selpoint.x);
3704 selend.x = 1+max(selanchor.x, selpoint.x);
3705 selstart.y = min(selanchor.y, selpoint.y);
3706 selend.y = max(selanchor.y, selpoint.y);
3709 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3710 if (selstate == DRAGGING) {
3712 * We've completed a selection. We now transfer the
3713 * data to the clipboard.
3715 clipme(selstart, selend, (seltype == RECTANGULAR));
3716 selstate = SELECTED;
3718 selstate = NO_SELECTION;
3719 } else if (b == MBT_PASTE
3721 #if MULTICLICK_ONLY_EVENT
3722 || a == MA_2CLK || a == MA_3CLK
3735 sfree(paste_buffer);
3740 int term_paste_pending(void)
3742 return paste_len != 0;
3747 static long last_paste = 0;
3748 long now, paste_diff;
3753 /* Don't wait forever to paste */
3755 now = GETTICKCOUNT();
3756 paste_diff = now - last_paste;
3757 if (paste_diff >= 0 && paste_diff < 450)
3762 while (paste_pos < paste_len) {
3764 while (n + paste_pos < paste_len) {
3765 if (paste_buffer[paste_pos + n++] == '\r')
3768 luni_send(paste_buffer + paste_pos, n, 0);
3771 if (paste_pos < paste_len) {
3776 sfree(paste_buffer);
3781 static void deselect(void)
3783 selstate = NO_SELECTION;
3784 selstart.x = selstart.y = selend.x = selend.y = 0;
3787 void term_deselect(void)
3793 int term_ldisc(int option)
3795 if (option == LD_ECHO)
3796 return term_echoing;
3797 if (option == LD_EDIT)
3798 return term_editing;
3803 * from_backend(), to get data from the backend for the terminal.
3805 int from_backend(int is_stderr, char *data, int len)
3809 bufchain_add(&inbuf, data, len);
3812 * term_out() always completely empties inbuf. Therefore,
3813 * there's no reason at all to return anything other than zero
3814 * from this function, because there _can't_ be a question of
3815 * the remote side needing to wait until term_out() has cleared
3818 * This is a slightly suboptimal way to deal with SSH2 - in
3819 * principle, the window mechanism would allow us to continue
3820 * to accept data on forwarded ports and X connections even
3821 * while the terminal processing was going slowly - but we
3822 * can't do the 100% right thing without moving the terminal
3823 * processing into a separate thread, and that might hurt
3824 * portability. So we manage stdout buffering the old SSH1 way:
3825 * if the terminal processing goes slowly, the whole SSH
3826 * connection stops accepting data until it's ready.
3828 * In practice, I can't imagine this causing serious trouble.