14 #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15 #define CL_VT100 0x0002 /* VT100 */
16 #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102 0x0008 /* VT102 */
18 #define CL_VT220 0x0010 /* VT220 */
19 #define CL_VT320 0x0020 /* VT320 */
20 #define CL_VT420 0x0040 /* VT420 */
21 #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
27 #define TM_VT100 (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29 #define TM_VT102 (TM_VT100AVO|CL_VT102)
30 #define TM_VT220 (TM_VT102|CL_VT220)
31 #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level = TM_PUTTY;
51 static tree234 *scrollback; /* lines scrolled off top of screen */
52 static tree234 *screen; /* lines on primary screen */
53 static tree234 *alt_screen; /* lines on alternate screen */
54 static int disptop; /* distance scrolled back (0 or -ve) */
56 static unsigned long *cpos; /* cursor position (convenience) */
58 static unsigned long *disptext; /* buffer of text on real screen */
59 static unsigned long *dispcurs; /* location of cursor on real screen */
60 static unsigned long curstype; /* type of cursor on real screen */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime *next;
68 static struct beeptime *beephead, *beeptail;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89 /* Product-order comparisons for rectangular block selection. */
90 #define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
91 #define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
93 static bufchain inbuf; /* terminal input buffer */
94 static pos curs; /* cursor */
95 static pos savecurs; /* saved cursor position */
96 static int marg_t, marg_b; /* scroll margins */
97 static int dec_om; /* DEC origin mode flag */
98 static int wrap, wrapnext; /* wrap flags */
99 static int insert; /* insert-mode flag */
100 static int cset; /* 0 or 1: which char set */
101 static int save_cset, save_csattr; /* saved with cursor position */
102 static int save_utf; /* saved with cursor position */
103 static int rvideo; /* global reverse video flag */
104 static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
105 static int cursor_on; /* cursor enabled flag */
106 static int reset_132; /* Flag ESC c resets to 80 cols */
107 static int use_bce; /* Use Background coloured erase */
108 static int blinker; /* When blinking is the cursor on ? */
109 static int tblinker; /* When the blinking text is on */
110 static int blink_is_real; /* Actually blink blinking text */
111 static int term_echoing; /* Does terminal want local echo? */
112 static int term_editing; /* Does terminal want local edit? */
113 static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
114 static int vt52_bold; /* Force bold on non-bold colours */
115 static int utf_state; /* Is there a pending UTF-8 character */
116 static int utf_char; /* and what is it so far. */
117 static int utf_size; /* The size of the UTF character. */
119 static int xterm_mouse; /* send mouse messages to app */
121 static unsigned long cset_attr[2];
124 * Saved settings on the alternate screen.
126 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
127 static int alt_t, alt_b;
128 static int alt_which;
130 #define ARGS_MAX 32 /* max # of esc sequence arguments */
131 #define ARG_DEFAULT 0 /* if an arg isn't specified */
132 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
133 static int esc_args[ARGS_MAX];
134 static int esc_nargs;
135 static int esc_query;
136 #define ANSI(x,y) ((x)+((y)<<8))
137 #define ANSI_QUE(x) ANSI(x,TRUE)
139 #define OSC_STR_MAX 2048
140 static int osc_strlen;
141 static char osc_string[OSC_STR_MAX + 1];
144 static char id_string[1024] = "\033[?6c";
146 static unsigned char *tabs;
158 OSC_STRING, OSC_MAYBE_ST,
167 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
170 LEXICOGRAPHIC, RECTANGULAR
173 SM_CHAR, SM_WORD, SM_LINE
175 static pos selstart, selend, selanchor;
177 static short wordness[256] = {
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
180 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
181 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
182 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
183 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
184 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
185 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
186 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
187 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
188 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
189 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
190 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
191 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
192 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
193 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
196 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
197 static wchar_t sel_nl[] = SEL_NL;
198 static wchar_t *paste_buffer = 0;
199 static int paste_len, paste_pos, paste_hold;
202 * Internal prototypes.
204 static void do_paint(Context, int);
205 static void erase_lots(int, int, int);
206 static void swap_screen(int);
207 static void update_sbar(void);
208 static void deselect(void);
209 /* log session to file stuff ... */
210 static FILE *lgfp = NULL;
211 static void logtraffic(unsigned char c, int logmode);
212 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
215 * Resize a line to make it `cols' columns wide.
217 unsigned long *resizeline(unsigned long *line, int cols)
220 unsigned long lineattrs;
222 if (line[0] != (unsigned long)cols) {
224 * This line is the wrong length, which probably means it
225 * hasn't been accessed since a resize. Resize it now.
228 lineattrs = line[oldlen + 1];
229 line = srealloc(line, TSIZE * (2 + cols));
231 for (i = oldlen; i < cols; i++)
232 line[i + 1] = ERASE_CHAR;
233 line[cols + 1] = lineattrs & LATTR_MODE;
240 * Retrieve a line of the screen or of the scrollback, according to
241 * whether the y coordinate is non-negative or negative
244 unsigned long *lineptr(int y, int lineno)
246 unsigned long *line, *newline;
254 whichtree = scrollback;
255 treeindex = y + count234(scrollback);
257 line = index234(whichtree, treeindex);
259 /* We assume that we don't screw up and retrieve something out of range. */
260 assert(line != NULL);
262 newline = resizeline(line, cols);
263 if (newline != line) {
264 delpos234(whichtree, treeindex);
265 addpos234(whichtree, newline, treeindex);
272 #define lineptr(x) lineptr(x,__LINE__)
274 * Set up power-on settings for the terminal.
276 static void power_on(void)
278 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
281 alt_b = marg_b = rows - 1;
286 for (i = 0; i < cols; i++)
287 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
289 alt_om = dec_om = cfg.dec_om;
290 alt_wnext = wrapnext = alt_ins = insert = FALSE;
291 alt_wrap = wrap = cfg.wrap_mode;
294 alt_sco_acs = sco_acs = 0;
295 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
300 save_attr = curr_attr = ATTR_DEFAULT;
301 term_editing = term_echoing = FALSE;
302 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
303 app_cursor_keys = cfg.app_cursor;
304 app_keypad_keys = cfg.app_keypad;
306 blink_is_real = cfg.blinktext;
307 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 * Clear the scrollback.
360 void term_clrsb(void)
364 while ((line = delpos234(scrollback, 0)) != NULL) {
371 * Initialise the terminal.
375 screen = alt_screen = scrollback = NULL;
377 disptext = dispcurs = NULL;
382 beephead = beeptail = NULL;
385 beep_overloaded = FALSE;
389 * Set up the terminal for a given size.
391 void term_size(int newrows, int newcols, int newsavelines)
394 unsigned long *newdisp, *line;
397 int save_alt_which = alt_which;
399 if (newrows == rows && newcols == cols && newsavelines == savelines)
400 return; /* nothing to do */
406 alt_b = marg_b = newrows - 1;
409 scrollback = newtree234(NULL);
410 screen = newtree234(NULL);
415 * Resize the screen and scrollback. We only need to shift
416 * lines around within our data structures, because lineptr()
417 * will take care of resizing each individual line if
420 * - If the new screen and the old screen differ in length, we
421 * must shunt some lines in from the scrollback or out to
424 * - If doing that fails to provide us with enough material to
425 * fill the new screen (i.e. the number of rows needed in
426 * the new screen exceeds the total number in the previous
427 * screen+scrollback), we must invent some blank lines to
430 * - Then, if the new scrollback length is less than the
431 * amount of scrollback we actually have, we must throw some
434 sblen = count234(scrollback);
435 /* Do this loop to expand the screen if newrows > rows */
436 for (i = rows; i < newrows; i++) {
438 line = delpos234(scrollback, --sblen);
440 line = smalloc(TSIZE * (newcols + 2));
442 for (j = 0; j <= newcols; j++)
443 line[j + 1] = ERASE_CHAR;
445 addpos234(screen, line, 0);
447 /* Do this loop to shrink the screen if newrows < rows */
448 for (i = newrows; i < rows; i++) {
449 line = delpos234(screen, 0);
450 addpos234(scrollback, line, sblen++);
452 assert(count234(screen) == newrows);
453 while (sblen > newsavelines) {
454 line = delpos234(scrollback, 0);
458 assert(count234(scrollback) <= newsavelines);
461 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
462 for (i = 0; i < newrows * (newcols + 1); i++)
463 newdisp[i] = ATTR_INVALID;
468 newalt = newtree234(NULL);
469 for (i = 0; i < newrows; i++) {
470 line = smalloc(TSIZE * (newcols + 2));
472 for (j = 0; j <= newcols; j++)
473 line[j + 1] = erase_char;
474 addpos234(newalt, line, i);
477 while (NULL != (line = delpos234(alt_screen, 0)))
479 freetree234(alt_screen);
483 tabs = srealloc(tabs, newcols * sizeof(*tabs));
486 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
487 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
491 curs.y += newrows - rows;
494 if (curs.y >= newrows)
495 curs.y = newrows - 1;
496 if (curs.x >= newcols)
497 curs.x = newcols - 1;
499 wrapnext = alt_wnext = FALSE;
503 savelines = newsavelines;
506 swap_screen(save_alt_which);
516 static void swap_screen(int which)
521 if (which == alt_which)
548 wrapnext = alt_wnext;
560 sco_acs = alt_sco_acs;
567 * Update the scroll bar.
569 static void update_sbar(void)
573 nscroll = count234(scrollback);
575 set_sbar(nscroll + rows, nscroll + disptop, rows);
579 * Check whether the region bounded by the two pointers intersects
580 * the scroll region, and de-select the on-screen selection if so.
582 static void check_selection(pos from, pos to)
584 if (poslt(from, selend) && poslt(selstart, to))
589 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
590 * for backward.) `sb' is TRUE if the scrolling is permitted to
591 * affect the scrollback buffer.
593 * NB this function invalidates all pointers into lines of the
594 * screen data structures. In particular, you MUST call fix_cpos
595 * after calling scroll() and before doing anything else that
596 * uses the cpos shortcut pointer.
598 static void scroll(int topline, int botline, int lines, int sb)
600 unsigned long *line, *line2;
603 if (topline != 0 || alt_which != 0)
608 line = delpos234(screen, botline);
609 line = resizeline(line, cols);
610 for (i = 0; i < cols; i++)
611 line[i + 1] = erase_char;
613 addpos234(screen, line, topline);
615 if (selstart.y >= topline && selstart.y <= botline) {
617 if (selstart.y > botline) {
618 selstart.y = botline;
622 if (selend.y >= topline && selend.y <= botline) {
624 if (selend.y > botline) {
634 line = delpos234(screen, topline);
635 if (sb && savelines > 0) {
636 int sblen = count234(scrollback);
638 * We must add this line to the scrollback. We'll
639 * remove a line from the top of the scrollback to
640 * replace it, or allocate a new one if the
641 * scrollback isn't full.
643 if (sblen == savelines) {
644 sblen--, line2 = delpos234(scrollback, 0);
646 line2 = smalloc(TSIZE * (cols + 2));
649 addpos234(scrollback, line, sblen);
653 * If the user is currently looking at part of the
654 * scrollback, and they haven't enabled any options
655 * that are going to reset the scrollback as a
656 * result of this movement, then the chances are
657 * they'd like to keep looking at the same line. So
658 * we move their viewpoint at the same rate as the
659 * scroll, at least until their viewpoint hits the
660 * top end of the scrollback buffer, at which point
661 * we don't have the choice any more.
663 * Thanks to Jan Holmen Holsten for the idea and
664 * initial implementation.
666 if (disptop > -savelines && disptop < 0)
669 line = resizeline(line, cols);
670 for (i = 0; i < cols; i++)
671 line[i + 1] = erase_char;
673 addpos234(screen, line, botline);
676 * If the selection endpoints move into the scrollback,
677 * we keep them moving until they hit the top. However,
678 * of course, if the line _hasn't_ moved into the
679 * scrollback then we don't do this, and cut them off
680 * at the top of the scroll region.
682 * This applies to selstart and selend (for an existing
683 * selection), and also selanchor (for one being
684 * selected as we speak).
686 seltop = sb ? -savelines : topline;
688 if (selstart.y >= seltop && selstart.y <= botline) {
690 if (selstart.y < seltop) {
695 if (selend.y >= seltop && selend.y <= botline) {
697 if (selend.y < seltop) {
702 if (selanchor.y >= seltop && selanchor.y <= botline) {
704 if (selanchor.y < seltop) {
705 selanchor.y = seltop;
716 * Move the cursor to a given position, clipping at boundaries. We
717 * may or may not want to clip at the scroll margin: marg_clip is 0
718 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
719 * even _being_ outside the margins.
721 static void move(int x, int y, int marg_clip)
728 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
730 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
744 * Save or restore the cursor and SGR mode.
746 static void save_cursor(int save)
750 save_attr = curr_attr;
753 save_csattr = cset_attr[cset];
754 save_sco_acs = sco_acs;
757 /* Make sure the window hasn't shrunk since the save */
763 curr_attr = save_attr;
766 cset_attr[cset] = save_csattr;
767 sco_acs = save_sco_acs;
770 erase_char = (' ' | ATTR_ASCII |
771 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
776 * Erase a large portion of the screen: the whole screen, or the
777 * whole line, or parts thereof.
779 static void erase_lots(int line_only, int from_begin, int to_end)
783 unsigned long *ldata;
805 check_selection(start, end);
807 /* Clear screen also forces a full window redraw, just in case. */
808 if (start.y == 0 && start.x == 0 && end.y == rows)
811 ldata = lineptr(start.y);
812 while (poslt(start, end)) {
813 if (start.x == cols && !erase_lattr)
814 ldata[start.x] &= ~LATTR_WRAPPED;
816 ldata[start.x] = erase_char;
817 if (incpos(start) && start.y < rows)
818 ldata = lineptr(start.y);
823 * Insert or delete characters within the current line. n is +ve if
824 * insertion is desired, and -ve for deletion.
826 static void insch(int n)
828 int dir = (n < 0 ? -1 : +1);
831 unsigned long *ldata;
833 n = (n < 0 ? -n : n);
834 if (n > cols - curs.x)
836 m = cols - curs.x - n;
838 cursplus.x = curs.x + n;
839 check_selection(curs, cursplus);
840 ldata = lineptr(curs.y);
842 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
844 ldata[curs.x + m++] = erase_char;
846 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
848 ldata[curs.x + n] = erase_char;
853 * Toggle terminal mode `mode' to state `state'. (`query' indicates
854 * whether the mode is a DEC private one or a normal one.)
856 static void toggle_mode(int mode, int query, int state)
862 case 1: /* application cursor keys */
863 app_cursor_keys = state;
865 case 2: /* VT52 mode */
868 blink_is_real = FALSE;
871 blink_is_real = cfg.blinktext;
874 case 3: /* 80/132 columns */
876 request_resize(state ? 132 : 80, rows);
879 case 5: /* reverse video */
881 * Toggle reverse video. If we receive an OFF within the
882 * visual bell timeout period after an ON, we trigger an
883 * effective visual bell, so that ESC[?5hESC[?5l will
884 * always be an actually _visible_ visual bell.
886 ticks = GetTickCount();
887 /* turn off a previous vbell to avoid inconsistencies */
888 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
890 if (rvideo && !state && /* we're turning it off... */
891 (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */
892 /* If there's no vbell timeout already, or this one lasts
893 * longer, replace vbell_timeout with ours. */
895 (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
896 vbell_startpoint = rvbell_startpoint;
897 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
898 } else if (!rvideo && state) {
899 /* This is an ON, so we notice the time and save it. */
900 rvbell_startpoint = ticks;
903 seen_disp_event = TRUE;
907 case 6: /* DEC origin mode */
910 case 7: /* auto wrap */
913 case 8: /* auto key repeat */
916 case 10: /* set local edit mode */
917 term_editing = state;
918 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
920 case 25: /* enable/disable cursor */
921 compatibility2(OTHER, VT220);
923 seen_disp_event = TRUE;
925 case 47: /* alternate screen */
926 compatibility(OTHER);
931 case 1000: /* xterm mouse 1 */
932 xterm_mouse = state ? 1 : 0;
933 set_raw_mouse_mode(state);
935 case 1002: /* xterm mouse 2 */
936 xterm_mouse = state ? 2 : 0;
937 set_raw_mouse_mode(state);
941 case 4: /* set insert mode */
942 compatibility(VT102);
945 case 12: /* set echo mode */
946 term_echoing = !state;
947 ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */
949 case 20: /* Return sends ... */
950 cr_lf_return = state;
952 case 34: /* Make cursor BIG */
953 compatibility2(OTHER, VT220);
959 * Process an OSC sequence: set window title or icon name.
961 static void do_osc(void)
965 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
967 osc_string[osc_strlen] = '\0';
968 switch (esc_args[0]) {
971 set_icon(osc_string);
972 if (esc_args[0] == 1)
974 /* fall through: parameter 0 means set both */
977 set_title(osc_string);
984 * Remove everything currently in `inbuf' and stick it up on the
985 * in-memory display. There's a big state machine in here to
986 * process escape sequences...
991 unsigned char localbuf[256], *chars;
996 while (nchars > 0 || bufchain_size(&inbuf) > 0) {
1000 bufchain_prefix(&inbuf, &ret, &nchars);
1001 if (nchars > sizeof(localbuf))
1002 nchars = sizeof(localbuf);
1003 memcpy(localbuf, ret, nchars);
1004 bufchain_consume(&inbuf, nchars);
1006 assert(chars != NULL);
1012 * Optionally log the session traffic to a file. Useful for
1013 * debugging and possibly also useful for actual logging.
1015 if (cfg.logtype == LGTYP_DEBUG)
1016 logtraffic((unsigned char) c, LGTYP_DEBUG);
1022 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
1023 * be able to display 8-bit characters, but I'll let that go 'cause
1027 /* First see about all those translations. */
1028 if (termstate == TOPLEVEL) {
1030 switch (utf_state) {
1033 /* UTF-8 must be stateless so we ignore iso2022. */
1034 if (unitab_ctrl[c] != 0xFF)
1036 else c = ((unsigned char)c) | ATTR_ASCII;
1038 } else if ((c & 0xe0) == 0xc0) {
1039 utf_size = utf_state = 1;
1040 utf_char = (c & 0x1f);
1041 } else if ((c & 0xf0) == 0xe0) {
1042 utf_size = utf_state = 2;
1043 utf_char = (c & 0x0f);
1044 } else if ((c & 0xf8) == 0xf0) {
1045 utf_size = utf_state = 3;
1046 utf_char = (c & 0x07);
1047 } else if ((c & 0xfc) == 0xf8) {
1048 utf_size = utf_state = 4;
1049 utf_char = (c & 0x03);
1050 } else if ((c & 0xfe) == 0xfc) {
1051 utf_size = utf_state = 5;
1052 utf_char = (c & 0x01);
1063 if ((c & 0xC0) != 0x80) {
1069 utf_char = (utf_char << 6) | (c & 0x3f);
1075 /* Is somebody trying to be evil! */
1077 (c < 0x800 && utf_size >= 2) ||
1078 (c < 0x10000 && utf_size >= 3) ||
1079 (c < 0x200000 && utf_size >= 4) ||
1080 (c < 0x4000000 && utf_size >= 5))
1083 /* Unicode line separator and paragraph separator are CR-LF */
1084 if (c == 0x2028 || c == 0x2029)
1087 /* High controls are probably a Baaad idea too. */
1091 /* The UTF-16 surrogates are not nice either. */
1092 /* The standard give the option of decoding these:
1093 * I don't want to! */
1094 if (c >= 0xD800 && c < 0xE000)
1097 /* ISO 10646 characters now limited to UTF-16 range. */
1101 /* This is currently a TagPhobic application.. */
1102 if (c >= 0xE0000 && c <= 0xE007F)
1105 /* U+FEFF is best seen as a null. */
1108 /* But U+FFFE is an error. */
1109 if (c == 0xFFFE || c == 0xFFFF)
1112 /* Oops this is a 16bit implementation */
1117 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1119 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1121 if (sco_acs == 2) c ^= 0x80;
1124 switch (cset_attr[cset]) {
1126 * Linedraw characters are different from 'ESC ( B'
1127 * only for a small range. For ones outside that
1128 * range, make sure we use the same font as well as
1129 * the same encoding.
1132 if (unitab_ctrl[c] != 0xFF)
1135 c = ((unsigned char) c) | ATTR_LINEDRW;
1139 /* If UK-ASCII, make the '#' a LineDraw Pound */
1141 c = '}' | ATTR_LINEDRW;
1144 /*FALLTHROUGH*/ case ATTR_ASCII:
1145 if (unitab_ctrl[c] != 0xFF)
1148 c = ((unsigned char) c) | ATTR_ASCII;
1151 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1157 /* How about C1 controls ? */
1158 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1159 has_compat(VT220)) {
1160 termstate = SEEN_ESC;
1162 c = '@' + (c & 0x1F);
1165 /* Or the GL control. */
1166 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1167 if (curs.x && !wrapnext)
1171 *cpos = (' ' | curr_attr | ATTR_ASCII);
1173 /* Or normal C0 controls. */
1174 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1176 case '\005': /* terminal type query */
1177 /* Strictly speaking this is VT100 but a VT100 defaults to
1178 * no response. Other terminals respond at their option.
1180 * Don't put a CR in the default string as this tends to
1181 * upset some weird software.
1183 * An xterm returns "xterm" (5 characters)
1185 compatibility(ANSIMIN);
1187 char abuf[256], *s, *d;
1189 for (s = cfg.answerback, d = abuf; *s; s++) {
1191 if (*s >= 'a' && *s <= 'z')
1192 *d++ = (*s - ('a' - 1));
1193 else if ((*s >= '@' && *s <= '_') ||
1194 *s == '?' || (*s & 0x80))
1199 } else if (*s == '^') {
1204 lpage_send(CP_ACP, abuf, d - abuf, 0);
1209 struct beeptime *newbeep;
1210 unsigned long ticks;
1212 ticks = GetTickCount();
1214 if (!beep_overloaded) {
1215 newbeep = smalloc(sizeof(struct beeptime));
1216 newbeep->ticks = ticks;
1217 newbeep->next = NULL;
1221 beeptail->next = newbeep;
1227 * Throw out any beeps that happened more than
1231 beephead->ticks < ticks - cfg.bellovl_t) {
1232 struct beeptime *tmp = beephead;
1233 beephead = tmp->next;
1240 if (cfg.bellovl && beep_overloaded &&
1241 ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
1243 * If we're currently overloaded and the
1244 * last beep was more than s seconds ago,
1245 * leave overload mode.
1247 beep_overloaded = FALSE;
1248 } else if (cfg.bellovl && !beep_overloaded &&
1249 nbeeps >= cfg.bellovl_n) {
1251 * Now, if we have n or more beeps
1252 * remaining in the queue, go into overload
1255 beep_overloaded = TRUE;
1260 * Perform an actual beep if we're not overloaded.
1262 if (!cfg.bellovl || !beep_overloaded) {
1264 if (cfg.beep == BELL_VISUAL) {
1266 vbell_startpoint = ticks;
1274 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1275 else if (curs.x == 0 && curs.y > 0)
1276 curs.x = cols - 1, curs.y--;
1282 seen_disp_event = TRUE;
1285 compatibility(VT100);
1289 compatibility(VT100);
1294 termstate = VT52_ESC;
1296 compatibility(ANSIMIN);
1297 termstate = SEEN_ESC;
1305 seen_disp_event = TRUE;
1307 logtraffic((unsigned char) c, LGTYP_ASCII);
1310 if (has_compat(SCOANSI)) {
1312 erase_lots(FALSE, FALSE, TRUE);
1315 seen_disp_event = 1;
1319 compatibility(VT100);
1321 if (curs.y == marg_b)
1322 scroll(marg_t, marg_b, 1, TRUE);
1323 else if (curs.y < rows - 1)
1329 seen_disp_event = 1;
1331 logtraffic((unsigned char) c, LGTYP_ASCII);
1335 pos old_curs = curs;
1336 unsigned long *ldata = lineptr(curs.y);
1340 } while (curs.x < cols - 1 && !tabs[curs.x]);
1342 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1343 if (curs.x >= cols / 2)
1344 curs.x = cols / 2 - 1;
1351 check_selection(old_curs, curs);
1353 seen_disp_event = TRUE;
1357 switch (termstate) {
1359 /* Only graphic characters get this far, ctrls are stripped above */
1360 if (wrapnext && wrap) {
1361 cpos[1] |= LATTR_WRAPPED;
1362 if (curs.y == marg_b)
1363 scroll(marg_t, marg_b, 1, TRUE);
1364 else if (curs.y < rows - 1)
1372 if (selstate != NO_SELECTION) {
1373 pos cursplus = curs;
1375 check_selection(curs, cursplus);
1377 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1378 logtraffic((unsigned char) c, LGTYP_ASCII);
1380 extern int wcwidth(wchar_t ucs);
1385 width = wcwidth((wchar_t) c);
1388 *cpos++ = c | curr_attr;
1389 if (++curs.x == cols) {
1390 *cpos |= LATTR_WRAPPED;
1391 if (curs.y == marg_b)
1392 scroll(marg_t, marg_b, 1, TRUE);
1393 else if (curs.y < rows - 1)
1398 *cpos++ = UCSWIDE | curr_attr;
1401 *cpos++ = c | curr_attr;
1408 if (curs.x == cols) {
1412 if (wrap && vt52_mode) {
1413 cpos[1] |= LATTR_WRAPPED;
1414 if (curs.y == marg_b)
1415 scroll(marg_t, marg_b, 1, TRUE);
1416 else if (curs.y < rows - 1)
1423 seen_disp_event = 1;
1428 * This state is virtually identical to SEEN_ESC, with the
1429 * exception that we have an OSC sequence in the pipeline,
1430 * and _if_ we see a backslash, we process it.
1434 termstate = TOPLEVEL;
1437 /* else fall through */
1439 if (c >= ' ' && c <= '/') {
1446 termstate = TOPLEVEL;
1447 switch (ANSI(c, esc_query)) {
1448 case '[': /* enter CSI mode */
1449 termstate = SEEN_CSI;
1451 esc_args[0] = ARG_DEFAULT;
1454 case ']': /* xterm escape sequences */
1455 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1456 compatibility(OTHER);
1457 termstate = SEEN_OSC;
1460 case '7': /* save cursor */
1461 compatibility(VT100);
1464 case '8': /* restore cursor */
1465 compatibility(VT100);
1467 seen_disp_event = TRUE;
1470 compatibility(VT100);
1471 app_keypad_keys = TRUE;
1474 compatibility(VT100);
1475 app_keypad_keys = FALSE;
1477 case 'D': /* exactly equivalent to LF */
1478 compatibility(VT100);
1479 if (curs.y == marg_b)
1480 scroll(marg_t, marg_b, 1, TRUE);
1481 else if (curs.y < rows - 1)
1485 seen_disp_event = TRUE;
1487 case 'E': /* exactly equivalent to CR-LF */
1488 compatibility(VT100);
1490 if (curs.y == marg_b)
1491 scroll(marg_t, marg_b, 1, TRUE);
1492 else if (curs.y < rows - 1)
1496 seen_disp_event = TRUE;
1498 case 'M': /* reverse index - backwards LF */
1499 compatibility(VT100);
1500 if (curs.y == marg_t)
1501 scroll(marg_t, marg_b, -1, TRUE);
1502 else if (curs.y > 0)
1506 seen_disp_event = TRUE;
1508 case 'Z': /* terminal type query */
1509 compatibility(VT100);
1510 ldisc_send(id_string, strlen(id_string), 0);
1512 case 'c': /* restore power-on settings */
1513 compatibility(VT100);
1516 request_resize(80, rows);
1521 seen_disp_event = TRUE;
1523 case 'H': /* set a tab */
1524 compatibility(VT100);
1525 tabs[curs.x] = TRUE;
1528 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1529 compatibility(VT100);
1531 unsigned long *ldata;
1535 for (i = 0; i < rows; i++) {
1537 for (j = 0; j < cols; j++)
1538 ldata[j] = ATTR_DEFAULT | 'E';
1542 seen_disp_event = TRUE;
1543 scrtop.x = scrtop.y = 0;
1546 check_selection(scrtop, scrbot);
1550 case ANSI('3', '#'):
1551 case ANSI('4', '#'):
1552 case ANSI('5', '#'):
1553 case ANSI('6', '#'):
1554 compatibility(VT100);
1556 unsigned long nlattr;
1557 unsigned long *ldata;
1558 switch (ANSI(c, esc_query)) {
1559 case ANSI('3', '#'):
1562 case ANSI('4', '#'):
1565 case ANSI('5', '#'):
1566 nlattr = LATTR_NORM;
1568 default: /* spiritually case ANSI('6', '#'): */
1569 nlattr = LATTR_WIDE;
1572 ldata = lineptr(curs.y);
1573 ldata[cols] &= ~LATTR_MODE;
1574 ldata[cols] |= nlattr;
1578 case ANSI('A', '('):
1579 compatibility(VT100);
1580 cset_attr[0] = ATTR_GBCHR;
1582 case ANSI('B', '('):
1583 compatibility(VT100);
1584 cset_attr[0] = ATTR_ASCII;
1586 case ANSI('0', '('):
1587 compatibility(VT100);
1588 cset_attr[0] = ATTR_LINEDRW;
1590 case ANSI('U', '('):
1591 compatibility(OTHER);
1592 cset_attr[0] = ATTR_SCOACS;
1595 case ANSI('A', ')'):
1596 compatibility(VT100);
1597 cset_attr[1] = ATTR_GBCHR;
1599 case ANSI('B', ')'):
1600 compatibility(VT100);
1601 cset_attr[1] = ATTR_ASCII;
1603 case ANSI('0', ')'):
1604 compatibility(VT100);
1605 cset_attr[1] = ATTR_LINEDRW;
1607 case ANSI('U', ')'):
1608 compatibility(OTHER);
1609 cset_attr[1] = ATTR_SCOACS;
1612 case ANSI('8', '%'): /* Old Linux code */
1613 case ANSI('G', '%'):
1614 compatibility(OTHER);
1617 case ANSI('@', '%'):
1618 compatibility(OTHER);
1624 termstate = TOPLEVEL; /* default */
1626 if (esc_nargs <= ARGS_MAX) {
1627 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1628 esc_args[esc_nargs - 1] = 0;
1629 esc_args[esc_nargs - 1] =
1630 10 * esc_args[esc_nargs - 1] + c - '0';
1632 termstate = SEEN_CSI;
1633 } else if (c == ';') {
1634 if (++esc_nargs <= ARGS_MAX)
1635 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1636 termstate = SEEN_CSI;
1637 } else if (c < '@') {
1644 termstate = SEEN_CSI;
1646 switch (ANSI(c, esc_query)) {
1647 case 'A': /* move up N lines */
1648 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1649 seen_disp_event = TRUE;
1651 case 'e': /* move down N lines */
1652 compatibility(ANSI);
1655 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1656 seen_disp_event = TRUE;
1658 case ANSI('c', '>'): /* report xterm version */
1659 compatibility(OTHER);
1660 /* this reports xterm version 136 so that VIM can
1661 use the drag messages from the mouse reporting */
1662 ldisc_send("\033[>0;136;0c", 11, 0);
1664 case 'a': /* move right N cols */
1665 compatibility(ANSI);
1668 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1669 seen_disp_event = TRUE;
1671 case 'D': /* move left N cols */
1672 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1673 seen_disp_event = TRUE;
1675 case 'E': /* move down N lines and CR */
1676 compatibility(ANSI);
1677 move(0, curs.y + def(esc_args[0], 1), 1);
1678 seen_disp_event = TRUE;
1680 case 'F': /* move up N lines and CR */
1681 compatibility(ANSI);
1682 move(0, curs.y - def(esc_args[0], 1), 1);
1683 seen_disp_event = TRUE;
1686 case '`': /* set horizontal posn */
1687 compatibility(ANSI);
1688 move(def(esc_args[0], 1) - 1, curs.y, 0);
1689 seen_disp_event = TRUE;
1691 case 'd': /* set vertical posn */
1692 compatibility(ANSI);
1694 (dec_om ? marg_t : 0) + def(esc_args[0],
1697 seen_disp_event = TRUE;
1700 case 'f': /* set horz and vert posns at once */
1702 esc_args[1] = ARG_DEFAULT;
1703 move(def(esc_args[1], 1) - 1,
1704 (dec_om ? marg_t : 0) + def(esc_args[0],
1707 seen_disp_event = TRUE;
1709 case 'J': /* erase screen or parts of it */
1711 unsigned int i = def(esc_args[0], 0) + 1;
1714 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1717 seen_disp_event = TRUE;
1719 case 'K': /* erase line or parts of it */
1721 unsigned int i = def(esc_args[0], 0) + 1;
1724 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1726 seen_disp_event = TRUE;
1728 case 'L': /* insert lines */
1729 compatibility(VT102);
1730 if (curs.y <= marg_b)
1731 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1734 seen_disp_event = TRUE;
1736 case 'M': /* delete lines */
1737 compatibility(VT102);
1738 if (curs.y <= marg_b)
1739 scroll(curs.y, marg_b, def(esc_args[0], 1),
1742 seen_disp_event = TRUE;
1744 case '@': /* insert chars */
1745 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1746 compatibility(VT102);
1747 insch(def(esc_args[0], 1));
1748 seen_disp_event = TRUE;
1750 case 'P': /* delete chars */
1751 compatibility(VT102);
1752 insch(-def(esc_args[0], 1));
1753 seen_disp_event = TRUE;
1755 case 'c': /* terminal type query */
1756 compatibility(VT100);
1757 /* This is the response for a VT102 */
1758 ldisc_send(id_string, strlen(id_string), 0);
1760 case 'n': /* cursor position query */
1761 if (esc_args[0] == 6) {
1763 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1765 ldisc_send(buf, strlen(buf), 0);
1766 } else if (esc_args[0] == 5) {
1767 ldisc_send("\033[0n", 4, 0);
1770 case 'h': /* toggle modes to high */
1772 compatibility(VT100);
1775 for (i = 0; i < esc_nargs; i++)
1776 toggle_mode(esc_args[i], esc_query, TRUE);
1779 case 'l': /* toggle modes to low */
1781 compatibility(VT100);
1784 for (i = 0; i < esc_nargs; i++)
1785 toggle_mode(esc_args[i], esc_query, FALSE);
1788 case 'g': /* clear tabs */
1789 compatibility(VT100);
1790 if (esc_nargs == 1) {
1791 if (esc_args[0] == 0) {
1792 tabs[curs.x] = FALSE;
1793 } else if (esc_args[0] == 3) {
1795 for (i = 0; i < cols; i++)
1800 case 'r': /* set scroll margins */
1801 compatibility(VT100);
1802 if (esc_nargs <= 2) {
1804 top = def(esc_args[0], 1) - 1;
1805 bot = (esc_nargs <= 1
1807 0 ? rows : def(esc_args[1], rows)) - 1;
1810 /* VTTEST Bug 9 - if region is less than 2 lines
1811 * don't change region.
1813 if (bot - top > 0) {
1818 * I used to think the cursor should be
1819 * placed at the top of the newly marginned
1820 * area. Apparently not: VMS TPU falls over
1823 * Well actually it should for Origin mode - RDB
1825 curs.y = (dec_om ? marg_t : 0);
1827 seen_disp_event = TRUE;
1831 case 'm': /* set graphics rendition */
1834 * A VT100 without the AVO only had one attribute, either
1835 * underline or reverse video depending on the cursor type,
1836 * this was selected by CSI 7m.
1839 * This is sometimes DIM, eg on the GIGI and Linux
1841 * This is sometimes INVIS various ANSI.
1843 * This like 22 disables BOLD, DIM and INVIS
1845 * The ANSI colours appear on any terminal that has colour
1846 * (obviously) but the interaction between sgr0 and the
1847 * colours varies but is usually related to the background
1848 * colour erase item.
1849 * The interaction between colour attributes and the mono
1850 * ones is also very implementation dependent.
1852 * The 39 and 49 attributes are likely to be unimplemented.
1855 for (i = 0; i < esc_nargs; i++) {
1856 switch (def(esc_args[i], 0)) {
1857 case 0: /* restore defaults */
1858 curr_attr = ATTR_DEFAULT;
1860 case 1: /* enable bold */
1861 compatibility(VT100AVO);
1862 curr_attr |= ATTR_BOLD;
1864 case 21: /* (enable double underline) */
1865 compatibility(OTHER);
1866 case 4: /* enable underline */
1867 compatibility(VT100AVO);
1868 curr_attr |= ATTR_UNDER;
1870 case 5: /* enable blink */
1871 compatibility(VT100AVO);
1872 curr_attr |= ATTR_BLINK;
1874 case 7: /* enable reverse video */
1875 curr_attr |= ATTR_REVERSE;
1877 case 10: /* SCO acs off */
1878 compatibility(SCOANSI);
1880 case 11: /* SCO acs on */
1881 compatibility(SCOANSI);
1883 case 12: /* SCO acs on flipped */
1884 compatibility(SCOANSI);
1886 case 22: /* disable bold */
1887 compatibility2(OTHER, VT220);
1888 curr_attr &= ~ATTR_BOLD;
1890 case 24: /* disable underline */
1891 compatibility2(OTHER, VT220);
1892 curr_attr &= ~ATTR_UNDER;
1894 case 25: /* disable blink */
1895 compatibility2(OTHER, VT220);
1896 curr_attr &= ~ATTR_BLINK;
1898 case 27: /* disable reverse video */
1899 compatibility2(OTHER, VT220);
1900 curr_attr &= ~ATTR_REVERSE;
1911 curr_attr &= ~ATTR_FGMASK;
1913 (esc_args[i] - 30) << ATTR_FGSHIFT;
1915 case 39: /* default-foreground */
1916 curr_attr &= ~ATTR_FGMASK;
1917 curr_attr |= ATTR_DEFFG;
1928 curr_attr &= ~ATTR_BGMASK;
1930 (esc_args[i] - 40) << ATTR_BGSHIFT;
1932 case 49: /* default-background */
1933 curr_attr &= ~ATTR_BGMASK;
1934 curr_attr |= ATTR_DEFBG;
1939 erase_char = (' ' | ATTR_ASCII |
1941 (ATTR_FGMASK | ATTR_BGMASK)));
1944 case 's': /* save cursor */
1947 case 'u': /* restore cursor */
1949 seen_disp_event = TRUE;
1951 case 't': /* set page size - ie window height */
1953 * VT340/VT420 sequence DECSLPP, DEC only allows values
1954 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1955 * illegal values (eg first arg 1..9) for window changing
1959 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1960 compatibility(VT340TEXT);
1961 request_resize(cols, def(esc_args[0], 24));
1963 } else if (esc_nargs >= 1 &&
1966 compatibility(OTHER);
1968 switch (esc_args[0]) {
1978 if (esc_nargs >= 3) {
1979 move_window(def(esc_args[1], 0),
1980 def(esc_args[2], 0));
1984 /* We should resize the window to a given
1985 * size in pixels here, but currently our
1986 * resizing code isn't healthy enough to
1990 set_zorder(TRUE); /* move to top */
1993 set_zorder(FALSE); /* move to bottom */
1999 if (esc_nargs >= 3) {
2000 request_resize(def(esc_args[1], cfg.width),
2001 def(esc_args[2], cfg.height));
2006 set_zoomed(esc_args[1] ? TRUE : FALSE);
2009 ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
2013 get_window_pos(&x, &y);
2014 len = sprintf(buf, "\033[3;%d;%dt", x, y);
2015 ldisc_send(buf, len, 0);
2018 get_window_pixels(&x, &y);
2019 len = sprintf(buf, "\033[4;%d;%dt", x, y);
2020 ldisc_send(buf, len, 0);
2023 len = sprintf(buf, "\033[8;%d;%dt",
2025 ldisc_send(buf, len, 0);
2029 * Hmmm. Strictly speaking we
2030 * should return `the size of the
2031 * screen in characters', but
2032 * that's not easy: (a) window
2033 * furniture being what it is it's
2034 * hard to compute, and (b) in
2035 * resize-font mode maximising the
2036 * window wouldn't change the
2037 * number of characters. *shrug*. I
2038 * think we'll ignore it for the
2039 * moment and see if anyone
2040 * complains, and then ask them
2041 * what they would like it to do.
2045 p = get_window_title(TRUE);
2047 ldisc_send("\033]L", 3, 0);
2048 ldisc_send(p, len, 0);
2049 ldisc_send("\033\\", 2, 0);
2052 p = get_window_title(FALSE);
2054 ldisc_send("\033]l", 3, 0);
2055 ldisc_send(p, len, 0);
2056 ldisc_send("\033\\", 2, 0);
2062 compatibility(SCOANSI);
2063 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
2066 seen_disp_event = TRUE;
2069 compatibility(SCOANSI);
2070 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
2073 seen_disp_event = TRUE;
2075 case ANSI('|', '*'):
2076 /* VT420 sequence DECSNLS
2077 * Set number of lines on screen
2078 * VT420 uses VGA like hardware and can support any size in
2079 * reasonable range (24..49 AIUI) with no default specified.
2081 compatibility(VT420);
2082 if (esc_nargs == 1 && esc_args[0] > 0) {
2083 request_resize(cols, def(esc_args[0], cfg.height));
2087 case ANSI('|', '$'):
2088 /* VT340/VT420 sequence DECSCPP
2089 * Set number of columns per page
2090 * Docs imply range is only 80 or 132, but I'll allow any.
2092 compatibility(VT340TEXT);
2093 if (esc_nargs <= 1) {
2094 request_resize(def(esc_args[0], cfg.width), rows);
2098 case 'X': /* write N spaces w/o moving cursor */
2099 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
2100 compatibility(ANSIMIN);
2102 int n = def(esc_args[0], 1);
2104 unsigned long *p = cpos;
2105 if (n > cols - curs.x)
2109 check_selection(curs, cursplus);
2112 seen_disp_event = TRUE;
2115 case 'x': /* report terminal characteristics */
2116 compatibility(VT100);
2119 int i = def(esc_args[0], 0);
2120 if (i == 0 || i == 1) {
2121 strcpy(buf, "\033[2;1;1;112;112;1;0x");
2123 ldisc_send(buf, 20, 0);
2127 case 'Z': /* BackTab for xterm */
2128 compatibility(OTHER);
2130 int i = def(esc_args[0], 1);
2131 pos old_curs = curs;
2133 for(;i>0 && curs.x>0; i--) {
2136 } while (curs.x >0 && !tabs[curs.x]);
2139 check_selection(old_curs, curs);
2142 case ANSI('L', '='):
2143 compatibility(OTHER);
2144 use_bce = (esc_args[0] <= 0);
2145 erase_char = ERASE_CHAR;
2147 erase_char = (' ' | ATTR_ASCII |
2149 (ATTR_FGMASK | ATTR_BGMASK)));
2151 case ANSI('E', '='):
2152 compatibility(OTHER);
2153 blink_is_real = (esc_args[0] >= 1);
2155 case ANSI('p', '"'):
2156 /* Allow the host to make this emulator a 'perfect' VT102.
2157 * This first appeared in the VT220, but we do need to get
2158 * back to PuTTY mode so I won't check it.
2160 * The arg in 40..42,50 are a PuTTY extension.
2161 * The 2nd arg, 8bit vs 7bit is not checked.
2163 * Setting VT102 mode should also change the Fkeys to
2164 * generate PF* codes as a real VT102 has no Fkeys.
2165 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2168 * Note ESC c will NOT change this!
2171 switch (esc_args[0]) {
2173 compatibility_level &= ~TM_VTXXX;
2174 compatibility_level |= TM_VT102;
2177 compatibility_level &= ~TM_VTXXX;
2178 compatibility_level |= TM_VT220;
2182 if (esc_args[0] > 60 && esc_args[0] < 70)
2183 compatibility_level |= TM_VTXXX;
2187 compatibility_level &= TM_VTXXX;
2190 compatibility_level = TM_PUTTY;
2193 compatibility_level = TM_SCOANSI;
2197 compatibility_level = TM_PUTTY;
2203 /* Change the response to CSI c */
2204 if (esc_args[0] == 50) {
2207 strcpy(id_string, "\033[?");
2208 for (i = 1; i < esc_nargs; i++) {
2210 strcat(id_string, ";");
2211 sprintf(lbuf, "%d", esc_args[i]);
2212 strcat(id_string, lbuf);
2214 strcat(id_string, "c");
2217 /* Is this a good idea ?
2218 * Well we should do a soft reset at this point ...
2220 if (!has_compat(VT420) && has_compat(VT100)) {
2222 request_resize(132, 24);
2224 request_resize(80, 24);
2233 case 'P': /* Linux palette sequence */
2234 termstate = SEEN_OSC_P;
2237 case 'R': /* Linux palette reset */
2240 termstate = TOPLEVEL;
2242 case 'W': /* word-set */
2243 termstate = SEEN_OSC_W;
2256 esc_args[0] = 10 * esc_args[0] + c - '0';
2260 * Grotty hack to support xterm and DECterm title
2261 * sequences concurrently.
2263 if (esc_args[0] == 2) {
2267 /* else fall through */
2269 termstate = OSC_STRING;
2275 * This OSC stuff is EVIL. It takes just one character to get into
2276 * sysline mode and it's not initially obvious how to get out.
2277 * So I've added CR and LF as string aborts.
2278 * This shouldn't effect compatibility as I believe embedded
2279 * control characters are supposed to be interpreted (maybe?)
2280 * and they don't display anything useful anyway.
2284 if (c == '\n' || c == '\r') {
2285 termstate = TOPLEVEL;
2286 } else if (c == 0234 || c == '\007') {
2288 * These characters terminate the string; ST and BEL
2289 * terminate the sequence and trigger instant
2290 * processing of it, whereas ESC goes back to SEEN_ESC
2291 * mode unless it is followed by \, in which case it is
2292 * synonymous with ST in the first place.
2295 termstate = TOPLEVEL;
2296 } else if (c == '\033')
2297 termstate = OSC_MAYBE_ST;
2298 else if (osc_strlen < OSC_STR_MAX)
2299 osc_string[osc_strlen++] = c;
2303 int max = (osc_strlen == 0 ? 21 : 16);
2305 if (c >= '0' && c <= '9')
2307 else if (c >= 'A' && c <= 'A' + max - 10)
2309 else if (c >= 'a' && c <= 'a' + max - 10)
2312 termstate = TOPLEVEL;
2315 osc_string[osc_strlen++] = val;
2316 if (osc_strlen >= 7) {
2317 palette_set(osc_string[0],
2318 osc_string[1] * 16 + osc_string[2],
2319 osc_string[3] * 16 + osc_string[4],
2320 osc_string[5] * 16 + osc_string[6]);
2322 termstate = TOPLEVEL;
2338 esc_args[0] = 10 * esc_args[0] + c - '0';
2341 termstate = OSC_STRING;
2346 termstate = TOPLEVEL;
2347 seen_disp_event = TRUE;
2350 move(curs.x, curs.y - 1, 1);
2353 move(curs.x, curs.y + 1, 1);
2356 move(curs.x + 1, curs.y, 1);
2359 move(curs.x - 1, curs.y, 1);
2362 * From the VT100 Manual
2363 * NOTE: The special graphics characters in the VT100
2364 * are different from those in the VT52
2366 * From VT102 manual:
2367 * 137 _ Blank - Same
2368 * 140 ` Reserved - Humm.
2369 * 141 a Solid rectangle - Similar
2370 * 142 b 1/ - Top half of fraction for the
2371 * 143 c 3/ - subscript numbers below.
2374 * 146 f Degrees - Same
2375 * 147 g Plus or minus - Same
2377 * 151 i Ellipsis (dots)
2380 * 154 l Bar at scan 0
2381 * 155 m Bar at scan 1
2382 * 156 n Bar at scan 2
2383 * 157 o Bar at scan 3 - Similar
2384 * 160 p Bar at scan 4 - Similar
2385 * 161 q Bar at scan 5 - Similar
2386 * 162 r Bar at scan 6 - Same
2387 * 163 s Bar at scan 7 - Similar
2402 cset_attr[cset = 0] = ATTR_LINEDRW;
2405 cset_attr[cset = 0] = ATTR_ASCII;
2412 scroll(0, rows - 1, -1, TRUE);
2413 else if (curs.y > 0)
2419 erase_lots(FALSE, FALSE, TRUE);
2423 erase_lots(TRUE, FALSE, TRUE);
2427 /* XXX Print cursor line */
2430 /* XXX Start controller mode */
2433 /* XXX Stop controller mode */
2437 termstate = VT52_Y1;
2440 ldisc_send("\033/Z", 3, 0);
2443 app_keypad_keys = TRUE;
2446 app_keypad_keys = FALSE;
2449 /* XXX This should switch to VT100 mode not current or default
2450 * VT mode. But this will only have effect in a VT220+
2454 blink_is_real = cfg.blinktext;
2458 /* XXX Enter auto print mode */
2461 /* XXX Exit auto print mode */
2464 /* XXX Print screen */
2470 /* compatibility(ATARI) */
2472 erase_lots(FALSE, FALSE, TRUE);
2476 /* compatibility(ATARI) */
2477 if (curs.y <= marg_b)
2478 scroll(curs.y, marg_b, -1, FALSE);
2481 /* compatibility(ATARI) */
2482 if (curs.y <= marg_b)
2483 scroll(curs.y, marg_b, 1, TRUE);
2486 /* compatibility(ATARI) */
2487 termstate = VT52_FG;
2490 /* compatibility(ATARI) */
2491 termstate = VT52_BG;
2494 /* compatibility(ATARI) */
2495 erase_lots(FALSE, TRUE, FALSE);
2499 /* compatibility(ATARI) */
2503 /* compatibility(ATARI) */
2506 /* case 'j': Save cursor position - broken on ST */
2507 /* case 'k': Restore cursor position */
2509 /* compatibility(ATARI) */
2510 erase_lots(TRUE, TRUE, TRUE);
2516 /* compatibility(ATARI) */
2517 erase_lots(TRUE, TRUE, FALSE);
2520 /* compatibility(ATARI) */
2521 curr_attr |= ATTR_REVERSE;
2524 /* compatibility(ATARI) */
2525 curr_attr &= ~ATTR_REVERSE;
2527 case 'v': /* wrap Autowrap on - Wyse style */
2528 /* compatibility(ATARI) */
2531 case 'w': /* Autowrap off */
2532 /* compatibility(ATARI) */
2537 /* compatibility(OTHER) */
2539 curr_attr = ATTR_DEFAULT;
2541 erase_char = (' ' | ATTR_ASCII |
2543 (ATTR_FGMASK | ATTR_BGMASK)));
2546 /* compatibility(VI50) */
2547 curr_attr |= ATTR_UNDER;
2550 /* compatibility(VI50) */
2551 curr_attr &= ~ATTR_UNDER;
2554 /* compatibility(VI50) */
2556 curr_attr |= ATTR_BOLD;
2559 /* compatibility(VI50) */
2561 curr_attr &= ~ATTR_BOLD;
2567 termstate = VT52_Y2;
2568 move(curs.x, c - ' ', 0);
2571 termstate = TOPLEVEL;
2572 move(c - ' ', curs.y, 0);
2577 termstate = TOPLEVEL;
2578 curr_attr &= ~ATTR_FGMASK;
2579 curr_attr &= ~ATTR_BOLD;
2580 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2581 if ((c & 0x8) || vt52_bold)
2582 curr_attr |= ATTR_BOLD;
2585 erase_char = (' ' | ATTR_ASCII |
2586 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2589 termstate = TOPLEVEL;
2590 curr_attr &= ~ATTR_BGMASK;
2591 curr_attr &= ~ATTR_BLINK;
2592 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2594 /* Note: bold background */
2596 curr_attr |= ATTR_BLINK;
2599 erase_char = (' ' | ATTR_ASCII |
2600 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
2603 default: break; /* placate gcc warning about enum use */
2605 if (selstate != NO_SELECTION) {
2606 pos cursplus = curs;
2608 check_selection(curs, cursplus);
2615 * Compare two lines to determine whether they are sufficiently
2616 * alike to scroll-optimise one to the other. Return the degree of
2619 static int linecmp(unsigned long *a, unsigned long *b)
2623 for (i = n = 0; i < cols; i++)
2624 n += (*a++ == *b++);
2630 * Given a context, update the window. Out of paranoia, we don't
2631 * allow WM_PAINT responses to do scrolling optimisations.
2633 static void do_paint(Context ctx, int may_optimise)
2635 int i, j, our_curs_y;
2636 unsigned long rv, cursor;
2639 long cursor_background = ERASE_CHAR;
2640 unsigned long ticks;
2643 * Check the visual bell state.
2646 ticks = GetTickCount();
2647 if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
2651 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2654 * screen array, disptop, scrtop,
2656 * cfg.blinkpc, blink_is_real, tblinker,
2657 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2660 /* Has the cursor position or type changed ? */
2663 if (blinker || !cfg.blink_cur)
2664 cursor = TATTR_ACTCURS;
2668 cursor = TATTR_PASCURS;
2670 cursor |= TATTR_RIGHTCURS;
2673 our_curs_y = curs.y - disptop;
2675 if (dispcurs && (curstype != cursor ||
2677 disptext + our_curs_y * (cols + 1) + curs.x)) {
2678 if (dispcurs > disptext &&
2679 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2680 dispcurs[-1] |= ATTR_INVALID;
2681 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2682 dispcurs[1] |= ATTR_INVALID;
2683 *dispcurs |= ATTR_INVALID;
2688 /* The normal screen data */
2689 for (i = 0; i < rows; i++) {
2690 unsigned long *ldata;
2692 int idx, dirty_line, dirty_run, selected;
2693 unsigned long attr = 0;
2694 int updated_line = 0;
2697 int last_run_dirty = 0;
2699 scrpos.y = i + disptop;
2700 ldata = lineptr(scrpos.y);
2701 lattr = (ldata[cols] & LATTR_MODE);
2703 idx = i * (cols + 1);
2704 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2705 disptext[idx + cols] = ldata[cols];
2707 for (j = 0; j < cols; j++, idx++) {
2708 unsigned long tattr, tchar;
2709 unsigned long *d = ldata + j;
2713 tchar = (*d & (CHAR_MASK | CSET_MASK));
2714 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2715 switch (tchar & CSET_MASK) {
2717 tchar = unitab_line[tchar & 0xFF];
2720 tchar = unitab_xterm[tchar & 0xFF];
2723 tchar = unitab_scoacs[tchar&0xFF];
2726 tattr |= (tchar & CSET_MASK);
2728 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2731 /* Video reversing things */
2732 if (seltype == LEXICOGRAPHIC)
2733 selected = posle(selstart, scrpos) && poslt(scrpos, selend);
2735 selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
2737 ^ (selected ? ATTR_REVERSE : 0));
2739 /* 'Real' blinking ? */
2740 if (blink_is_real && (tattr & ATTR_BLINK)) {
2741 if (has_focus && tblinker) {
2743 tattr &= ~CSET_MASK;
2746 tattr &= ~ATTR_BLINK;
2750 * Check the font we'll _probably_ be using to see if
2751 * the character is wide when we don't want it to be.
2753 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2754 if ((tattr & ATTR_WIDE) == 0 &&
2755 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2756 tattr |= ATTR_NARROW;
2757 } else if (disptext[idx]&ATTR_NARROW)
2758 tattr |= ATTR_NARROW;
2760 /* Cursor here ? Save the 'background' */
2761 if (i == our_curs_y && j == curs.x) {
2762 cursor_background = tattr | tchar;
2763 dispcurs = disptext + idx;
2766 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2769 break_run = (tattr != attr || j - start >= sizeof(ch));
2771 /* Special hack for VT100 Linedraw glyphs */
2772 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2773 && tchar <= 0xBD) break_run = TRUE;
2775 if (!dbcs_screenfont && !dirty_line) {
2776 if ((tchar | tattr) == disptext[idx])
2778 else if (!dirty_run && ccount == 1)
2783 if ((dirty_run || last_run_dirty) && ccount > 0) {
2784 do_text(ctx, start, i, ch, ccount, attr, lattr);
2790 if (dbcs_screenfont)
2791 last_run_dirty = dirty_run;
2792 dirty_run = dirty_line;
2795 if ((tchar | tattr) != disptext[idx])
2797 ch[ccount++] = (char) tchar;
2798 disptext[idx] = tchar | tattr;
2800 /* If it's a wide char step along to the next one. */
2801 if (tattr & ATTR_WIDE) {
2805 /* Cursor is here ? Ouch! */
2806 if (i == our_curs_y && j == curs.x) {
2807 cursor_background = *d;
2808 dispcurs = disptext + idx;
2810 if (disptext[idx] != *d)
2816 if (dirty_run && ccount > 0) {
2817 do_text(ctx, start, i, ch, ccount, attr, lattr);
2821 /* Cursor on this line ? (and changed) */
2822 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2823 ch[0] = (char) (cursor_background & CHAR_MASK);
2824 attr = (cursor_background & ATTR_MASK) | cursor;
2825 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2832 * Flick the switch that says if blinking things should be shown or hidden.
2835 void term_blink(int flg)
2837 static long last_blink = 0;
2838 static long last_tblink = 0;
2839 long now, blink_diff;
2841 now = GetTickCount();
2842 blink_diff = now - last_tblink;
2844 /* Make sure the text blinks no more than 2Hz */
2845 if (blink_diff < 0 || blink_diff > 450) {
2847 tblinker = !tblinker;
2856 blink_diff = now - last_blink;
2858 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2859 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2867 * Invalidate the whole screen so it will be repainted in full.
2869 void term_invalidate(void)
2873 for (i = 0; i < rows * (cols + 1); i++)
2874 disptext[i] = ATTR_INVALID;
2878 * Paint the window in response to a WM_PAINT message.
2880 void term_paint(Context ctx, int left, int top, int right, int bottom)
2883 if (left < 0) left = 0;
2884 if (top < 0) top = 0;
2885 if (right >= cols) right = cols-1;
2886 if (bottom >= rows) bottom = rows-1;
2888 for (i = top; i <= bottom && i < rows; i++) {
2889 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2890 for (j = left; j <= right && j < cols; j++)
2891 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2893 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2894 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2897 /* This should happen soon enough, also for some reason it sometimes
2898 * fails to actually do anything when re-sizing ... painting the wrong
2902 do_paint (ctx, FALSE);
2906 * Attempt to scroll the scrollback. The second parameter gives the
2907 * position we want to scroll to; the first is +1 to denote that
2908 * this position is relative to the beginning of the scrollback, -1
2909 * to denote it is relative to the end, and 0 to denote that it is
2910 * relative to the current position.
2912 void term_scroll(int rel, int where)
2914 int sbtop = -count234(scrollback);
2916 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2917 if (disptop < sbtop)
2925 static void clipme(pos top, pos bottom, int rect)
2928 wchar_t *wbptr; /* where next char goes within workbuf */
2930 int wblen = 0; /* workbuf len */
2931 int buflen; /* amount of memory allocated to workbuf */
2933 buflen = 5120; /* Default size */
2934 workbuf = smalloc(buflen * sizeof(wchar_t));
2935 wbptr = workbuf; /* start filling here */
2936 old_top_x = top.x; /* needed for rect==1 */
2938 while (poslt(top, bottom)) {
2940 unsigned long *ldata = lineptr(top.y);
2944 * nlpos will point at the maximum position on this line we
2945 * should copy up to. So we start it at the end of the
2952 * ... move it backwards if there's unused space at the end
2953 * of the line (and also set `nl' if this is the case,
2954 * because in normal selection mode this means we need a
2955 * newline at the end)...
2957 if (!(ldata[cols] & LATTR_WRAPPED)) {
2958 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2959 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2960 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2961 && poslt(top, nlpos))
2963 if (poslt(nlpos, bottom))
2968 * ... and then clip it to the terminal x coordinate if
2969 * we're doing rectangular selection. (In this case we
2970 * still did the above, so that copying e.g. the right-hand
2971 * column from a table doesn't fill with spaces on the
2975 if (nlpos.x > bottom.x)
2977 nl = (top.y < bottom.y);
2980 while (poslt(top, bottom) && poslt(top, nlpos)) {
2983 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2985 wchar_t cbuf[16], *p;
2986 int uc = (ldata[top.x] & 0xFFFF);
2989 if (uc == UCSWIDE) {
2994 switch (uc & CSET_MASK) {
2997 uc = unitab_xterm[uc & 0xFF];
3001 uc = unitab_line[uc & 0xFF];
3004 uc = unitab_scoacs[uc&0xFF];
3007 switch (uc & CSET_MASK) {
3009 uc = unitab_font[uc & 0xFF];
3012 uc = unitab_oemcp[uc & 0xFF];
3016 set = (uc & CSET_MASK);
3017 c = (uc & CHAR_MASK);
3021 if (DIRECT_FONT(uc)) {
3022 if (c >= ' ' && c != 0x7F) {
3023 unsigned char buf[4];
3026 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
3028 buf[1] = (unsigned char) ldata[top.x + 1];
3029 rv = MultiByteToWideChar(font_codepage,
3030 0, buf, 2, wbuf, 4);
3034 rv = MultiByteToWideChar(font_codepage,
3035 0, buf, 1, wbuf, 4);
3039 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
3046 for (p = cbuf; *p; p++) {
3047 /* Enough overhead for trailing NL and nul */
3048 if (wblen >= buflen - 16) {
3051 sizeof(wchar_t) * (buflen += 100));
3052 wbptr = workbuf + wblen;
3061 for (i = 0; i < sel_nl_sz; i++) {
3063 *wbptr++ = sel_nl[i];
3067 top.x = rect ? old_top_x : 0;
3071 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
3072 if (buflen > 0) /* indicates we allocated this buffer */
3076 void term_copyall(void)
3079 top.y = -count234(scrollback);
3081 clipme(top, curs, 0);
3085 * The wordness array is mainly for deciding the disposition of the US-ASCII
3088 static int wordtype(int uc)
3091 int start, end, ctype;
3092 } *wptr, ucs_words[] = {
3098 0x037e, 0x037e, 1}, /* Greek question mark */
3100 0x0387, 0x0387, 1}, /* Greek ano teleia */
3102 0x055a, 0x055f, 1}, /* Armenian punctuation */
3104 0x0589, 0x0589, 1}, /* Armenian full stop */
3106 0x0700, 0x070d, 1}, /* Syriac punctuation */
3108 0x104a, 0x104f, 1}, /* Myanmar punctuation */
3110 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
3112 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
3114 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
3116 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
3118 0x1800, 0x180a, 1}, /* Mongolian punctuation */
3120 0x2000, 0x200a, 0}, /* Various spaces */
3122 0x2070, 0x207f, 2}, /* superscript */
3124 0x2080, 0x208f, 2}, /* subscript */
3126 0x200b, 0x27ff, 1}, /* punctuation and symbols */
3128 0x3000, 0x3000, 0}, /* ideographic space */
3130 0x3001, 0x3020, 1}, /* ideographic punctuation */
3132 0x303f, 0x309f, 3}, /* Hiragana */
3134 0x30a0, 0x30ff, 3}, /* Katakana */
3136 0x3300, 0x9fff, 3}, /* CJK Ideographs */
3138 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
3140 0xf900, 0xfaff, 3}, /* CJK Ideographs */
3142 0xfe30, 0xfe6b, 1}, /* punctuation forms */
3144 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
3146 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
3148 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
3150 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
3152 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
3157 uc &= (CSET_MASK | CHAR_MASK);
3159 switch (uc & CSET_MASK) {
3161 uc = unitab_xterm[uc & 0xFF];
3164 uc = unitab_line[uc & 0xFF];
3167 uc = unitab_scoacs[uc&0xFF];
3170 switch (uc & CSET_MASK) {
3172 uc = unitab_font[uc & 0xFF];
3175 uc = unitab_oemcp[uc & 0xFF];
3179 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3180 * fail as there's such a thing as a double width space. :-(
3182 if (dbcs_screenfont && font_codepage == line_codepage)
3186 return wordness[uc];
3188 for (wptr = ucs_words; wptr->start; wptr++) {
3189 if (uc >= wptr->start && uc <= wptr->end)
3197 * Spread the selection outwards according to the selection mode.
3199 static pos sel_spread_half(pos p, int dir)
3201 unsigned long *ldata;
3203 int topy = -count234(scrollback);
3205 ldata = lineptr(p.y);
3210 * In this mode, every character is a separate unit, except
3211 * for runs of spaces at the end of a non-wrapping line.
3213 if (!(ldata[cols] & LATTR_WRAPPED)) {
3214 unsigned long *q = ldata + cols;
3215 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
3217 if (q == ldata + cols)
3219 if (p.x >= q - ldata)
3220 p.x = (dir == -1 ? q - ldata : cols - 1);
3225 * In this mode, the units are maximal runs of characters
3226 * whose `wordness' has the same value.
3228 wvalue = wordtype(ldata[p.x]);
3232 if (wordtype(ldata[p.x + 1]) == wvalue)
3237 if (ldata[cols] & LATTR_WRAPPED) {
3238 unsigned long *ldata2;
3239 ldata2 = lineptr(p.y+1);
3240 if (wordtype(ldata2[0]) == wvalue) {
3253 if (wordtype(ldata[p.x - 1]) == wvalue)
3258 unsigned long *ldata2;
3261 ldata2 = lineptr(p.y-1);
3262 if ((ldata2[cols] & LATTR_WRAPPED) &&
3263 wordtype(ldata2[cols-1]) == wvalue) {
3275 * In this mode, every line is a unit.
3277 p.x = (dir == -1 ? 0 : cols - 1);
3283 static void sel_spread(void)
3285 if (seltype == LEXICOGRAPHIC) {
3286 selstart = sel_spread_half(selstart, -1);
3288 selend = sel_spread_half(selend, +1);
3293 void term_do_paste(void)
3298 get_clip(&data, &len);
3303 sfree(paste_buffer);
3304 paste_pos = paste_hold = paste_len = 0;
3305 paste_buffer = smalloc(len * sizeof(wchar_t));
3308 while (p < data + len) {
3309 while (p < data + len &&
3310 !(p <= data + len - sel_nl_sz &&
3311 !memcmp(p, sel_nl, sizeof(sel_nl))))
3316 for (i = 0; i < p - q; i++) {
3317 paste_buffer[paste_len++] = q[i];
3321 if (p <= data + len - sel_nl_sz &&
3322 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3323 paste_buffer[paste_len++] = '\r';
3329 /* Assume a small paste will be OK in one go. */
3330 if (paste_len < 256) {
3331 luni_send(paste_buffer, paste_len, 0);
3333 sfree(paste_buffer);
3335 paste_pos = paste_hold = paste_len = 0;
3338 get_clip(NULL, NULL);
3341 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3342 int shift, int ctrl, int alt)
3345 unsigned long *ldata;
3346 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
3347 int default_seltype;
3351 if (a == MA_DRAG && !raw_mouse)
3356 if (a == MA_DRAG && !raw_mouse)
3369 selpoint.y = y + disptop;
3371 ldata = lineptr(selpoint.y);
3372 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3376 int encstate = 0, r, c;
3378 static int is_down = 0;
3382 encstate = 0x20; /* left button down */
3393 case MBT_WHEEL_DOWN:
3396 default: break; /* placate gcc warning about enum use */
3400 if (xterm_mouse == 1)
3413 default: break; /* placate gcc warning about enum use */
3422 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3423 ldisc_send(abuf, 6, 0);
3427 b = translate_button(b);
3430 * Set the selection type (rectangular or normal) at the start
3431 * of a selection attempt, from the state of Alt.
3433 if (!alt ^ !cfg.rect_select)
3434 default_seltype = RECTANGULAR;
3436 default_seltype = LEXICOGRAPHIC;
3438 if (selstate == NO_SELECTION) {
3439 seltype = default_seltype;
3442 if (b == MBT_SELECT && a == MA_CLICK) {
3444 selstate = ABOUT_TO;
3445 seltype = default_seltype;
3446 selanchor = selpoint;
3448 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3450 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3451 selstate = DRAGGING;
3452 selstart = selanchor = selpoint;
3456 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3457 (b == MBT_EXTEND && a != MA_RELEASE)) {
3458 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3460 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3461 if (seltype == LEXICOGRAPHIC) {
3463 * For normal selection, we extend by moving
3464 * whichever end of the current selection is closer
3467 if (posdiff(selpoint, selstart) <
3468 posdiff(selend, selstart) / 2) {
3472 selanchor = selstart;
3476 * For rectangular selection, we have a choice of
3477 * _four_ places to put selanchor and selpoint: the
3478 * four corners of the selection.
3480 if (2*selpoint.x < selstart.x + selend.x)
3481 selanchor.x = selend.x-1;
3483 selanchor.x = selstart.x;
3485 if (2*selpoint.y < selstart.y + selend.y)
3486 selanchor.y = selend.y;
3488 selanchor.y = selstart.y;
3490 selstate = DRAGGING;
3492 if (selstate != ABOUT_TO && selstate != DRAGGING)
3493 selanchor = selpoint;
3494 selstate = DRAGGING;
3495 if (seltype == LEXICOGRAPHIC) {
3497 * For normal selection, we set (selstart,selend) to
3498 * (selpoint,selanchor) in some order.
3500 if (poslt(selpoint, selanchor)) {
3501 selstart = selpoint;
3505 selstart = selanchor;
3511 * For rectangular selection, we may need to
3512 * interchange x and y coordinates (if the user has
3513 * dragged in the -x and +y directions, or vice versa).
3515 selstart.x = min(selanchor.x, selpoint.x);
3516 selend.x = 1+max(selanchor.x, selpoint.x);
3517 selstart.y = min(selanchor.y, selpoint.y);
3518 selend.y = max(selanchor.y, selpoint.y);
3521 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3522 if (selstate == DRAGGING) {
3524 * We've completed a selection. We now transfer the
3525 * data to the clipboard.
3527 clipme(selstart, selend, (seltype == RECTANGULAR));
3528 selstate = SELECTED;
3530 selstate = NO_SELECTION;
3531 } else if (b == MBT_PASTE
3532 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3543 sfree(paste_buffer);
3550 static long last_paste = 0;
3551 long now, paste_diff;
3556 /* Don't wait forever to paste */
3558 now = GetTickCount();
3559 paste_diff = now - last_paste;
3560 if (paste_diff >= 0 && paste_diff < 450)
3565 while (paste_pos < paste_len) {
3567 while (n + paste_pos < paste_len) {
3568 if (paste_buffer[paste_pos + n++] == '\r')
3571 luni_send(paste_buffer + paste_pos, n, 0);
3574 if (paste_pos < paste_len) {
3579 sfree(paste_buffer);
3584 static void deselect(void)
3586 selstate = NO_SELECTION;
3587 selstart.x = selstart.y = selend.x = selend.y = 0;
3590 void term_deselect(void)
3596 int term_ldisc(int option)
3598 if (option == LD_ECHO)
3599 return term_echoing;
3600 if (option == LD_EDIT)
3601 return term_editing;
3606 * from_backend(), to get data from the backend for the terminal.
3608 int from_backend(int is_stderr, char *data, int len)
3610 bufchain_add(&inbuf, data, len);
3613 * term_out() always completely empties inbuf. Therefore,
3614 * there's no reason at all to return anything other than zero
3615 * from this function, because there _can't_ be a question of
3616 * the remote side needing to wait until term_out() has cleared
3619 * This is a slightly suboptimal way to deal with SSH2 - in
3620 * principle, the window mechanism would allow us to continue
3621 * to accept data on forwarded ports and X connections even
3622 * while the terminal processing was going slowly - but we
3623 * can't do the 100% right thing without moving the terminal
3624 * processing into a separate thread, and that might hurt
3625 * portability. So we manage stdout buffering the old SSH1 way:
3626 * if the terminal processing goes slowly, the whole SSH
3627 * connection stops accepting data until it's ready.
3629 * In practice, I can't imagine this causing serious trouble.
3635 * Log session traffic.
3637 void logtraffic(unsigned char c, int logmode)
3639 if (cfg.logtype > 0) {
3640 if (cfg.logtype == logmode) {
3641 /* deferred open file from pgm start? */
3650 void settimstr(char *ta, int no_sec);
3651 char *subslfcode(char *dest, char *src, char *dstrt);
3652 char *stpncpy(char *dst, const char *src, size_t maxlen);
3654 char currlogfilename[FILENAME_MAX];
3656 /* open log file append/overwrite mode */
3666 sprintf(writemod, "wb"); /* default to rewrite */
3669 tm = *localtime(&t);
3671 /* substitute special codes in file name */
3672 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3674 lgfp = fopen(currlogfilename, "r"); /* file already present? */
3678 i = askappend(currlogfilename);
3680 writemod[0] = 'a'; /* set append mode */
3681 else if (i == 0) { /* cancelled */
3683 cfg.logtype = 0; /* disable logging */
3688 lgfp = fopen(currlogfilename, writemod);
3689 if (lgfp) { /* enter into event log */
3690 sprintf(buf, "%s session log (%s mode) to file : ",
3691 (writemod[0] == 'a') ? "Appending" : "Writing new",
3692 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3693 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3694 /* Make sure we do not exceed the output buffer size */
3695 strncat(buf, currlogfilename, 128);
3696 buf[strlen(buf)] = '\0';
3699 /* --- write header line into log file */
3700 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3701 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
3703 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3707 void logfclose(void)
3716 * translate format codes into time/date strings
3717 * and insert them into log file name
3719 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3721 static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3722 char buf[10], *bufp;
3724 char *ds = d; /* save start pos. */
3725 int len = FILENAME_MAX-1;
3728 /* Let (bufp, len) be the string to append. */
3729 bufp = buf; /* don't usually override this */
3733 if (*s) switch (c = *s++, tolower(c)) {
3735 size = strftime(buf, sizeof(buf), "%Y", tm);
3738 size = strftime(buf, sizeof(buf), "%m", tm);
3741 size = strftime(buf, sizeof(buf), "%d", tm);
3744 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3748 size = strlen(bufp);
3762 memcpy(d, bufp, size);