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 *wanttext; /* buffer of text we want on screen */
59 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
62 struct beeptime *next;
65 static struct beeptime *beephead, *beeptail;
70 static unsigned char *selspace; /* buffer for building selections in */
72 #define TSIZE (sizeof(unsigned long))
73 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
75 static unsigned long curr_attr, save_attr;
76 static unsigned long erase_char = ERASE_CHAR;
81 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
82 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
83 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
84 #define posdiff(p1,p2) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x )
85 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
86 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
88 static pos curs; /* cursor */
89 static pos savecurs; /* saved cursor position */
90 static int marg_t, marg_b; /* scroll margins */
91 static int dec_om; /* DEC origin mode flag */
92 static int wrap, wrapnext; /* wrap flags */
93 static int insert; /* insert-mode flag */
94 static int cset; /* 0 or 1: which char set */
95 static int save_cset, save_csattr; /* saved with cursor position */
96 static int rvideo; /* global reverse video flag */
97 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
98 static int cursor_on; /* cursor enabled flag */
99 static int reset_132; /* Flag ESC c resets to 80 cols */
100 static int use_bce; /* Use Background coloured erase */
101 static int blinker; /* When blinking is the cursor on ? */
102 static int tblinker; /* When the blinking text is on */
103 static int blink_is_real; /* Actually blink blinking text */
104 static int term_echoing; /* Does terminal want local echo? */
105 static int term_editing; /* Does terminal want local edit? */
107 static int xterm_mouse; /* send mouse messages to app */
109 static unsigned long cset_attr[2];
112 * Saved settings on the alternate screen.
114 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
115 static int alt_t, alt_b;
116 static int alt_which;
118 #define ARGS_MAX 32 /* max # of esc sequence arguments */
119 #define ARG_DEFAULT 0 /* if an arg isn't specified */
120 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
121 static int esc_args[ARGS_MAX];
122 static int esc_nargs;
123 static int esc_query;
124 #define ANSI(x,y) ((x)+((y)<<8))
125 #define ANSI_QUE(x) ANSI(x,TRUE)
127 #define OSC_STR_MAX 2048
128 static int osc_strlen;
129 static char osc_string[OSC_STR_MAX + 1];
132 static char id_string[1024] = "\033[?6c";
134 static unsigned char *tabs;
148 OSC_STRING, OSC_MAYBE_ST,
156 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
159 SM_CHAR, SM_WORD, SM_LINE
161 static pos selstart, selend, selanchor;
163 static short wordness[256] = {
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
166 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
167 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
168 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
169 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
170 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
172 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
173 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
174 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
175 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
176 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
177 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
178 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
179 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
182 static unsigned char sel_nl[] = SEL_NL;
183 static char *paste_buffer = 0;
184 static int paste_len, paste_pos, paste_hold;
187 * Internal prototypes.
189 static void do_paint(Context, int);
190 static void erase_lots(int, int, int);
191 static void swap_screen(int);
192 static void update_sbar(void);
193 static void deselect(void);
194 /* log session to file stuff ... */
195 static FILE *lgfp = NULL;
196 static void logtraffic(unsigned char c, int logmode);
199 * Retrieve a line of the screen or of the scrollback, according to
200 * whether the y coordinate is non-negative or negative
203 unsigned long *lineptr(int y, int lineno)
205 unsigned long *line, lineattrs;
207 int i, treeindex, oldlen;
213 whichtree = scrollback;
214 treeindex = y + count234(scrollback);
216 line = index234(whichtree, treeindex);
218 /* We assume that we don't screw up and retrieve something out of range. */
219 assert(line != NULL);
221 if (line[0] != 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 delpos234(whichtree, treeindex);
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;
234 addpos234(whichtree, line, treeindex);
240 #define lineptr(x) lineptr(x,__LINE__)
242 * Set up power-on settings for the terminal.
244 static void power_on(void)
246 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
249 alt_b = marg_b = rows - 1;
254 for (i = 0; i < cols; i++)
255 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
257 alt_om = dec_om = cfg.dec_om;
258 alt_wnext = wrapnext = alt_ins = insert = FALSE;
259 alt_wrap = wrap = cfg.wrap_mode;
261 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
265 save_attr = curr_attr = ATTR_DEFAULT;
266 term_editing = term_echoing = FALSE;
267 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
268 app_cursor_keys = cfg.app_cursor;
269 app_keypad_keys = cfg.app_keypad;
271 blink_is_real = cfg.blinktext;
272 erase_char = ERASE_CHAR;
276 for (i = 0; i < 256; i++)
277 wordness[i] = cfg.wordness[i];
281 erase_lots(FALSE, TRUE, TRUE);
283 erase_lots(FALSE, TRUE, TRUE);
288 * Force a screen update.
290 void term_update(void)
295 if ((seen_key_event && (cfg.scroll_on_key)) ||
296 (seen_disp_event && (cfg.scroll_on_disp))) {
297 disptop = 0; /* return to main screen */
298 seen_disp_event = seen_key_event = 0;
302 sys_cursor(curs.x, curs.y - disptop);
308 * Same as power_on(), but an external function.
310 void term_pwron(void)
320 * Clear the scrollback.
322 void term_clrsb(void)
326 while ((line = delpos234(scrollback, 0)) != NULL) {
333 * Initialise the terminal.
337 screen = alt_screen = scrollback = NULL;
339 disptext = wanttext = NULL;
345 beephead = beeptail = NULL;
348 beep_overloaded = FALSE;
352 * Set up the terminal for a given size.
354 void term_size(int newrows, int newcols, int newsavelines)
356 tree234 *newsb, *newscreen, *newalt;
357 unsigned long *newdisp, *newwant, *oldline, *line;
360 int save_alt_which = alt_which;
362 if (newrows == rows && newcols == cols && newsavelines == savelines)
363 return; /* nothing to do */
369 alt_b = marg_b = newrows - 1;
372 scrollback = newtree234(NULL);
373 screen = newtree234(NULL);
378 * Resize the screen and scrollback. We only need to shift
379 * lines around within our data structures, because lineptr()
380 * will take care of resizing each individual line if
383 * - If the new screen and the old screen differ in length, we
384 * must shunt some lines in from the scrollback or out to
387 * - If doing that fails to provide us with enough material to
388 * fill the new screen (i.e. the number of rows needed in
389 * the new screen exceeds the total number in the previous
390 * screen+scrollback), we must invent some blank lines to
393 * - Then, if the new scrollback length is less than the
394 * amount of scrollback we actually have, we must throw some
397 sblen = count234(scrollback);
398 /* Do this loop to expand the screen if newrows > rows */
399 for (i = rows; i < newrows; i++) {
401 line = delpos234(scrollback, --sblen);
403 line = smalloc(TSIZE * (newcols + 2));
405 for (j = 0; j <= newcols; j++)
406 line[j + 1] = ERASE_CHAR;
408 addpos234(screen, line, 0);
410 /* Do this loop to shrink the screen if newrows < rows */
411 for (i = newrows; i < rows; i++) {
412 line = delpos234(screen, 0);
413 addpos234(scrollback, line, sblen++);
415 assert(count234(screen) == newrows);
416 while (sblen > newsavelines) {
417 line = delpos234(scrollback, 0);
421 assert(count234(scrollback) <= newsavelines);
424 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
425 for (i = 0; i < newrows * (newcols + 1); i++)
426 newdisp[i] = ATTR_INVALID;
430 newwant = smalloc(newrows * (newcols + 1) * TSIZE);
431 for (i = 0; i < newrows * (newcols + 1); i++)
432 newwant[i] = ATTR_INVALID;
436 newalt = newtree234(NULL);
437 for (i = 0; i < newrows; i++) {
438 line = smalloc(TSIZE * (newcols + 2));
440 for (j = 0; j <= newcols; j++)
441 line[j + 1] = erase_char;
442 addpos234(newalt, line, i);
445 while (NULL != (line = delpos234(alt_screen, 0)))
447 freetree234(alt_screen);
453 smalloc((newrows + newsavelines) * (newcols + sizeof(sel_nl)));
455 tabs = srealloc(tabs, newcols * sizeof(*tabs));
458 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
459 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
463 curs.y += newrows - rows;
466 if (curs.y >= newrows)
467 curs.y = newrows - 1;
468 if (curs.x >= newcols)
469 curs.x = newcols - 1;
471 wrapnext = alt_wnext = FALSE;
475 savelines = newsavelines;
478 swap_screen(save_alt_which);
487 static void swap_screen(int which)
492 if (which == alt_which)
519 wrapnext = alt_wnext;
532 * Update the scroll bar.
534 static void update_sbar(void)
538 nscroll = count234(scrollback);
540 set_sbar(nscroll + rows, nscroll + disptop, rows);
544 * Check whether the region bounded by the two pointers intersects
545 * the scroll region, and de-select the on-screen selection if so.
547 static void check_selection(pos from, pos to)
549 if (poslt(from, selend) && poslt(selstart, to))
554 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
555 * for backward.) `sb' is TRUE if the scrolling is permitted to
556 * affect the scrollback buffer.
558 * NB this function invalidates all pointers into lines of the
559 * screen data structures. In particular, you MUST call fix_cpos
560 * after calling scroll() and before doing anything else that
561 * uses the cpos shortcut pointer.
563 static void scroll(int topline, int botline, int lines, int sb)
565 unsigned long *line, *line2;
568 if (topline != 0 || alt_which != 0)
573 line = delpos234(screen, botline);
574 for (i = 0; i < cols; i++)
575 line[i + 1] = erase_char;
577 addpos234(screen, line, topline);
579 if (selstart.y >= topline && selstart.y <= botline) {
581 if (selstart.y > botline) {
582 selstart.y = botline;
586 if (selend.y >= topline && selend.y <= botline) {
588 if (selend.y > botline) {
598 line = delpos234(screen, topline);
599 if (sb && savelines > 0) {
600 int sblen = count234(scrollback);
602 * We must add this line to the scrollback. We'll
603 * remove a line from the top of the scrollback to
604 * replace it, or allocate a new one if the
605 * scrollback isn't full.
607 if (sblen == savelines) {
608 sblen--, line2 = delpos234(scrollback, 0);
610 line2 = smalloc(TSIZE * (cols + 2));
613 addpos234(scrollback, line, sblen);
616 for (i = 0; i < cols; i++)
617 line[i + 1] = erase_char;
619 addpos234(screen, line, botline);
621 if (selstart.y >= topline && selstart.y <= botline) {
623 if (selstart.y < topline) {
624 selstart.y = topline;
628 if (selend.y >= topline && selend.y <= botline) {
630 if (selend.y < topline) {
642 * Move the cursor to a given position, clipping at boundaries. We
643 * may or may not want to clip at the scroll margin: marg_clip is 0
644 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
645 * even _being_ outside the margins.
647 static void move(int x, int y, int marg_clip)
654 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
656 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
670 * Save or restore the cursor and SGR mode.
672 static void save_cursor(int save)
676 save_attr = curr_attr;
678 save_csattr = cset_attr[cset];
681 /* Make sure the window hasn't shrunk since the save */
687 curr_attr = save_attr;
689 cset_attr[cset] = save_csattr;
692 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
697 * Erase a large portion of the screen: the whole screen, or the
698 * whole line, or parts thereof.
700 static void erase_lots(int line_only, int from_begin, int to_end)
704 unsigned long *ldata;
725 check_selection(start, end);
727 /* Clear screen also forces a full window redraw, just in case. */
728 if (start.y == 0 && start.x == 0 && end.y == rows)
731 ldata = lineptr(start.y);
732 while (poslt(start, end)) {
733 if (start.y == cols && !erase_lattr)
734 ldata[start.x] &= ~ATTR_WRAPPED;
736 ldata[start.x] = erase_char;
737 if (incpos(start) && start.y < rows)
738 ldata = lineptr(start.y);
743 * Insert or delete characters within the current line. n is +ve if
744 * insertion is desired, and -ve for deletion.
746 static void insch(int n)
748 int dir = (n < 0 ? -1 : +1);
751 unsigned long *ldata;
753 n = (n < 0 ? -n : n);
754 if (n > cols - curs.x)
756 m = cols - curs.x - n;
758 cursplus.x = curs.x + n;
759 check_selection(curs, cursplus);
760 ldata = lineptr(curs.y);
762 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
764 ldata[curs.x + m++] = erase_char;
766 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
768 ldata[curs.x + n] = erase_char;
773 * Toggle terminal mode `mode' to state `state'. (`query' indicates
774 * whether the mode is a DEC private one or a normal one.)
776 static void toggle_mode(int mode, int query, int state)
782 case 1: /* application cursor keys */
783 app_cursor_keys = state;
785 case 2: /* VT52 mode */
788 case 3: /* 80/132 columns */
790 request_resize(state ? 132 : 80, rows, 1);
793 case 5: /* reverse video */
795 * Toggle reverse video. If we receive an OFF within the
796 * visual bell timeout period after an ON, we trigger an
797 * effective visual bell, so that ESC[?5hESC[?5l will
798 * always be an actually _visible_ visual bell.
800 ticks = GetTickCount();
801 if (rvideo && !state && /* we're turning it off */
802 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
803 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
804 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
805 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
806 } else if (!rvideo && state) {
807 /* This is an ON, so we notice the time and save it. */
808 rvbell_timeout = ticks + VBELL_TIMEOUT;
811 seen_disp_event = TRUE;
815 case 6: /* DEC origin mode */
818 case 7: /* auto wrap */
821 case 8: /* auto key repeat */
824 case 10: /* set local edit mode */
825 term_editing = state;
826 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
828 case 25: /* enable/disable cursor */
829 compatibility2(OTHER, VT220);
831 seen_disp_event = TRUE;
833 case 47: /* alternate screen */
834 compatibility(OTHER);
839 case 1000: /* xterm mouse 1 */
840 xterm_mouse = state ? 1 : 0;
841 set_raw_mouse_mode(state);
843 case 1002: /* xterm mouse 2 */
844 xterm_mouse = state ? 2 : 0;
845 set_raw_mouse_mode(state);
849 case 4: /* set insert mode */
850 compatibility(VT102);
853 case 12: /* set echo mode */
854 term_echoing = !state;
855 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
857 case 20: /* Return sends ... */
858 cr_lf_return = state;
864 * Process an OSC sequence: set window title or icon name.
866 static void do_osc(void)
870 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
872 osc_string[osc_strlen] = '\0';
873 switch (esc_args[0]) {
876 set_icon(osc_string);
877 if (esc_args[0] == 1)
879 /* fall through: parameter 0 means set both */
882 set_title(osc_string);
889 * Remove everything currently in `inbuf' and stick it up on the
890 * in-memory display. There's a big state machine in here to
891 * process escape sequences...
897 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
898 c = inbuf[inbuf_reap];
901 * Optionally log the session traffic to a file. Useful for
902 * debugging and possibly also useful for actual logging.
904 logtraffic((unsigned char) c, LGTYP_DEBUG);
906 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
907 * be able to display 8-bit characters, but I'll let that go 'cause
910 if (((c & 0x60) == 0 || c == '\177') &&
911 termstate < DO_CTRLS && ((c & 0x80) == 0 || has_compat(VT220))) {
913 case '\005': /* terminal type query */
914 /* Strictly speaking this is VT100 but a VT100 defaults to
915 * no response. Other terminals respond at their option.
917 * Don't put a CR in the default string as this tends to
918 * upset some weird software.
920 * An xterm returns "xterm" (5 characters)
922 compatibility(ANSIMIN);
924 char abuf[256], *s, *d;
926 for (s = cfg.answerback, d = abuf; *s; s++) {
928 if (*s >= 'a' && *s <= 'z')
929 *d++ = (*s - ('a' - 1));
930 else if ((*s >= '@' && *s <= '_') ||
931 *s == '?' || (*s & 0x80))
936 } else if (*s == '^') {
939 *d++ = xlat_kbd2tty((unsigned char) *s);
941 ldisc_send(abuf, d - abuf);
946 struct beeptime *newbeep;
949 ticks = GetTickCount();
951 if (!beep_overloaded) {
952 newbeep = smalloc(sizeof(struct beeptime));
953 newbeep->ticks = ticks;
954 newbeep->next = NULL;
958 beeptail->next = newbeep;
964 * Throw out any beeps that happened more than
968 beephead->ticks < ticks - cfg.bellovl_t) {
969 struct beeptime *tmp = beephead;
970 beephead = tmp->next;
977 if (cfg.bellovl && beep_overloaded &&
978 ticks - lastbeep >= cfg.bellovl_s) {
980 * If we're currently overloaded and the
981 * last beep was more than s seconds ago,
982 * leave overload mode.
984 beep_overloaded = FALSE;
985 } else if (cfg.bellovl && !beep_overloaded &&
986 nbeeps >= cfg.bellovl_n) {
988 * Now, if we have n or more beeps
989 * remaining in the queue, go into overload
992 beep_overloaded = TRUE;
997 * Perform an actual beep if we're not overloaded.
999 if ((!cfg.bellovl || !beep_overloaded)
1003 else if (cfg.beep == 2) {
1005 vbell_timeout = ticks + VBELL_TIMEOUT;
1013 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1014 else if (curs.x == 0 && curs.y > 0)
1015 curs.x = cols - 1, curs.y--;
1021 seen_disp_event = TRUE;
1024 compatibility(VT100);
1028 compatibility(VT100);
1033 termstate = VT52_ESC;
1035 compatibility(ANSIMIN);
1036 termstate = SEEN_ESC;
1040 compatibility(VT220);
1041 termstate = SEEN_CSI;
1043 esc_args[0] = ARG_DEFAULT;
1047 compatibility(VT220);
1048 termstate = SEEN_OSC;
1055 seen_disp_event = TRUE;
1057 logtraffic((unsigned char) c, LGTYP_ASCII);
1061 compatibility(VT100);
1063 if (curs.y == marg_b)
1064 scroll(marg_t, marg_b, 1, TRUE);
1065 else if (curs.y < rows - 1)
1071 seen_disp_event = 1;
1073 logtraffic((unsigned char) c, LGTYP_ASCII);
1077 pos old_curs = curs;
1078 unsigned long *ldata = lineptr(curs.y);
1082 } while (curs.x < cols - 1 && !tabs[curs.x]);
1084 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1085 if (curs.x >= cols / 2)
1086 curs.x = cols / 2 - 1;
1093 check_selection(old_curs, curs);
1095 seen_disp_event = TRUE;
1097 case '\177': /* Destructive backspace
1098 This does nothing on a real VT100 */
1099 compatibility(OTHER);
1100 if (curs.x && !wrapnext)
1104 *cpos = (' ' | curr_attr | ATTR_ASCII);
1108 switch (termstate) {
1110 /* Only graphic characters get this far, ctrls are stripped above */
1111 if (wrapnext && wrap) {
1112 cpos[1] |= ATTR_WRAPPED;
1113 if (curs.y == marg_b)
1114 scroll(marg_t, marg_b, 1, TRUE);
1115 else if (curs.y < rows - 1)
1123 if (selstate != NO_SELECTION) {
1124 pos cursplus = curs;
1126 check_selection(curs, cursplus);
1128 switch (cset_attr[cset]) {
1130 * Linedraw characters are different from 'ESC ( B'
1131 * only for a small range. For ones outside that
1132 * range, make sure we use the same font as well as
1133 * the same encoding.
1136 if (c < 0x5f || c > 0x7F)
1138 xlat_tty2scr((unsigned char) c) | curr_attr |
1141 *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1144 ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
1147 /* If UK-ASCII, make the '#' a LineDraw Pound */
1149 *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1152 /*FALLTHROUGH*/ default:
1153 *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
1154 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1155 logtraffic((unsigned char) c, LGTYP_ASCII);
1160 if (curs.x == cols) {
1165 seen_disp_event = 1;
1169 termstate = TOPLEVEL;
1173 * This state is virtually identical to SEEN_ESC, with the
1174 * exception that we have an OSC sequence in the pipeline,
1175 * and _if_ we see a backslash, we process it.
1179 termstate = TOPLEVEL;
1182 /* else fall through */
1184 termstate = TOPLEVEL;
1186 case ' ': /* some weird sequence? */
1187 compatibility(VT220);
1188 termstate = IGNORE_NEXT;
1190 case '[': /* enter CSI mode */
1191 termstate = SEEN_CSI;
1193 esc_args[0] = ARG_DEFAULT;
1196 case ']': /* xterm escape sequences */
1197 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1198 compatibility(OTHER);
1199 termstate = SEEN_OSC;
1202 case '(': /* should set GL */
1203 compatibility(VT100);
1206 case ')': /* should set GR */
1207 compatibility(VT100);
1210 case '7': /* save cursor */
1211 compatibility(VT100);
1214 case '8': /* restore cursor */
1215 compatibility(VT100);
1217 seen_disp_event = TRUE;
1220 compatibility(VT100);
1221 app_keypad_keys = TRUE;
1224 compatibility(VT100);
1225 app_keypad_keys = FALSE;
1227 case 'D': /* exactly equivalent to LF */
1228 compatibility(VT100);
1229 if (curs.y == marg_b)
1230 scroll(marg_t, marg_b, 1, TRUE);
1231 else if (curs.y < rows - 1)
1235 seen_disp_event = TRUE;
1237 case 'E': /* exactly equivalent to CR-LF */
1238 compatibility(VT100);
1240 if (curs.y == marg_b)
1241 scroll(marg_t, marg_b, 1, TRUE);
1242 else if (curs.y < rows - 1)
1246 seen_disp_event = TRUE;
1248 case 'M': /* reverse index - backwards LF */
1249 compatibility(VT100);
1250 if (curs.y == marg_t)
1251 scroll(marg_t, marg_b, -1, TRUE);
1252 else if (curs.y > 0)
1256 seen_disp_event = TRUE;
1258 case 'Z': /* terminal type query */
1259 compatibility(VT100);
1260 ldisc_send(id_string, strlen(id_string));
1262 case 'c': /* restore power-on settings */
1263 compatibility(VT100);
1266 request_resize(80, rows, 1);
1271 seen_disp_event = TRUE;
1273 case '#': /* ESC # 8 fills screen with Es :-) */
1274 compatibility(VT100);
1275 termstate = SEEN_ESCHASH;
1277 case 'H': /* set a tab */
1278 compatibility(VT100);
1279 tabs[curs.x] = TRUE;
1284 termstate = TOPLEVEL; /* default */
1286 if (esc_nargs <= ARGS_MAX) {
1287 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1288 esc_args[esc_nargs - 1] = 0;
1289 esc_args[esc_nargs - 1] =
1290 10 * esc_args[esc_nargs - 1] + c - '0';
1292 termstate = SEEN_CSI;
1293 } else if (c == ';') {
1294 if (++esc_nargs <= ARGS_MAX)
1295 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1296 termstate = SEEN_CSI;
1297 } else if (c < '@') {
1304 termstate = SEEN_CSI;
1306 switch (ANSI(c, esc_query)) {
1307 case 'A': /* move up N lines */
1308 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1309 seen_disp_event = TRUE;
1311 case 'e': /* move down N lines */
1312 compatibility(ANSI);
1314 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1315 seen_disp_event = TRUE;
1317 case 'a': /* move right N cols */
1318 compatibility(ANSI);
1319 case ANSI('c', '>'): /* report xterm version */
1320 compatibility(OTHER);
1321 /* this reports xterm version 136 so that VIM can
1322 use the drag messages from the mouse reporting */
1323 ldisc_send("\033[>0;136;0c", 11);
1326 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1327 seen_disp_event = TRUE;
1329 case 'D': /* move left N cols */
1330 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1331 seen_disp_event = TRUE;
1333 case 'E': /* move down N lines and CR */
1334 compatibility(ANSI);
1335 move(0, curs.y + def(esc_args[0], 1), 1);
1336 seen_disp_event = TRUE;
1338 case 'F': /* move up N lines and CR */
1339 compatibility(ANSI);
1340 move(0, curs.y - def(esc_args[0], 1), 1);
1341 seen_disp_event = TRUE;
1344 case '`': /* set horizontal posn */
1345 compatibility(ANSI);
1346 move(def(esc_args[0], 1) - 1, curs.y, 0);
1347 seen_disp_event = TRUE;
1349 case 'd': /* set vertical posn */
1350 compatibility(ANSI);
1352 (dec_om ? marg_t : 0) + def(esc_args[0],
1355 seen_disp_event = TRUE;
1358 case 'f': /* set horz and vert posns at once */
1360 esc_args[1] = ARG_DEFAULT;
1361 move(def(esc_args[1], 1) - 1,
1362 (dec_om ? marg_t : 0) + def(esc_args[0],
1365 seen_disp_event = TRUE;
1367 case 'J': /* erase screen or parts of it */
1369 unsigned int i = def(esc_args[0], 0) + 1;
1372 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1375 seen_disp_event = TRUE;
1377 case 'K': /* erase line or parts of it */
1379 unsigned int i = def(esc_args[0], 0) + 1;
1382 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1384 seen_disp_event = TRUE;
1386 case 'L': /* insert lines */
1387 compatibility(VT102);
1388 if (curs.y <= marg_b)
1389 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1392 seen_disp_event = TRUE;
1394 case 'M': /* delete lines */
1395 compatibility(VT102);
1396 if (curs.y <= marg_b)
1397 scroll(curs.y, marg_b, def(esc_args[0], 1),
1400 seen_disp_event = TRUE;
1402 case '@': /* insert chars */
1403 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1404 compatibility(VT102);
1405 insch(def(esc_args[0], 1));
1406 seen_disp_event = TRUE;
1408 case 'P': /* delete chars */
1409 compatibility(VT102);
1410 insch(-def(esc_args[0], 1));
1411 seen_disp_event = TRUE;
1413 case 'c': /* terminal type query */
1414 compatibility(VT100);
1415 /* This is the response for a VT102 */
1416 ldisc_send(id_string, strlen(id_string));
1418 case 'n': /* cursor position query */
1419 if (esc_args[0] == 6) {
1421 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1423 ldisc_send(buf, strlen(buf));
1424 } else if (esc_args[0] == 5) {
1425 ldisc_send("\033[0n", 4);
1428 case 'h': /* toggle modes to high */
1430 compatibility(VT100);
1433 for (i = 0; i < esc_nargs; i++)
1434 toggle_mode(esc_args[i], esc_query, TRUE);
1437 case 'l': /* toggle modes to low */
1439 compatibility(VT100);
1442 for (i = 0; i < esc_nargs; i++)
1443 toggle_mode(esc_args[i], esc_query, FALSE);
1446 case 'g': /* clear tabs */
1447 compatibility(VT100);
1448 if (esc_nargs == 1) {
1449 if (esc_args[0] == 0) {
1450 tabs[curs.x] = FALSE;
1451 } else if (esc_args[0] == 3) {
1453 for (i = 0; i < cols; i++)
1458 case 'r': /* set scroll margins */
1459 compatibility(VT100);
1460 if (esc_nargs <= 2) {
1462 top = def(esc_args[0], 1) - 1;
1463 bot = (esc_nargs <= 1
1465 0 ? rows : def(esc_args[1], rows)) - 1;
1468 /* VTTEST Bug 9 - if region is less than 2 lines
1469 * don't change region.
1471 if (bot - top > 0) {
1476 * I used to think the cursor should be
1477 * placed at the top of the newly marginned
1478 * area. Apparently not: VMS TPU falls over
1481 * Well actually it should for Origin mode - RDB
1483 curs.y = (dec_om ? marg_t : 0);
1485 seen_disp_event = TRUE;
1489 case 'm': /* set graphics rendition */
1492 * A VT100 without the AVO only had one attribute, either
1493 * underline or reverse video depending on the cursor type,
1494 * this was selected by CSI 7m.
1497 * This is DIM on the VT100-AVO and VT102
1499 * This is BLINK on the VT100-AVO and VT102+
1501 * This is INVIS on the VT100-AVO and VT102
1503 * This like 22 disables BOLD, DIM and INVIS
1505 * The ANSI colours appear on any terminal that has colour
1506 * (obviously) but the interaction between sgr0 and the
1507 * colours varies but is usually related to the background
1508 * colour erase item.
1509 * The interaction between colour attributes and the mono
1510 * ones is also very implementation dependent.
1512 * The 39 and 49 attributes are likely to be unimplemented.
1515 for (i = 0; i < esc_nargs; i++) {
1516 switch (def(esc_args[i], 0)) {
1517 case 0: /* restore defaults */
1518 curr_attr = ATTR_DEFAULT;
1520 case 1: /* enable bold */
1521 compatibility(VT100AVO);
1522 curr_attr |= ATTR_BOLD;
1524 case 21: /* (enable double underline) */
1525 compatibility(OTHER);
1526 case 4: /* enable underline */
1527 compatibility(VT100AVO);
1528 curr_attr |= ATTR_UNDER;
1530 case 5: /* enable blink */
1531 compatibility(VT100AVO);
1532 curr_attr |= ATTR_BLINK;
1534 case 7: /* enable reverse video */
1535 curr_attr |= ATTR_REVERSE;
1537 case 22: /* disable bold */
1538 compatibility2(OTHER, VT220);
1539 curr_attr &= ~ATTR_BOLD;
1541 case 24: /* disable underline */
1542 compatibility2(OTHER, VT220);
1543 curr_attr &= ~ATTR_UNDER;
1545 case 25: /* disable blink */
1546 compatibility2(OTHER, VT220);
1547 curr_attr &= ~ATTR_BLINK;
1549 case 27: /* disable reverse video */
1550 compatibility2(OTHER, VT220);
1551 curr_attr &= ~ATTR_REVERSE;
1562 curr_attr &= ~ATTR_FGMASK;
1564 (esc_args[i] - 30) << ATTR_FGSHIFT;
1566 case 39: /* default-foreground */
1567 curr_attr &= ~ATTR_FGMASK;
1568 curr_attr |= ATTR_DEFFG;
1579 curr_attr &= ~ATTR_BGMASK;
1581 (esc_args[i] - 40) << ATTR_BGSHIFT;
1583 case 49: /* default-background */
1584 curr_attr &= ~ATTR_BGMASK;
1585 curr_attr |= ATTR_DEFBG;
1593 (ATTR_FGMASK | ATTR_BGMASK |
1597 case 's': /* save cursor */
1600 case 'u': /* restore cursor */
1602 seen_disp_event = TRUE;
1604 case 't': /* set page size - ie window height */
1606 * VT340/VT420 sequence DECSLPP, DEC only allows values
1607 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1608 * illegal values (eg first arg 1..9) for window changing
1611 compatibility(VT340TEXT);
1613 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1614 request_resize(cols, def(esc_args[0], 24), 0);
1618 case ANSI('|', '*'):
1619 /* VT420 sequence DECSNLS
1620 * Set number of lines on screen
1621 * VT420 uses VGA like hardware and can support any size in
1622 * reasonable range (24..49 AIUI) with no default specified.
1624 compatibility(VT420);
1625 if (esc_nargs == 1 && esc_args[0] > 0) {
1626 request_resize(cols,
1627 def(esc_args[0], cfg.height),
1632 case ANSI('|', '$'):
1633 /* VT340/VT420 sequence DECSCPP
1634 * Set number of columns per page
1635 * Docs imply range is only 80 or 132, but I'll allow any.
1637 compatibility(VT340TEXT);
1638 if (esc_nargs <= 1) {
1639 request_resize(def(esc_args[0], cfg.width),
1644 case 'X': /* write N spaces w/o moving cursor */
1645 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1646 compatibility(ANSIMIN);
1648 int n = def(esc_args[0], 1);
1650 unsigned long *p = cpos;
1651 if (n > cols - curs.x)
1655 check_selection(curs, cursplus);
1658 seen_disp_event = TRUE;
1661 case 'x': /* report terminal characteristics */
1662 compatibility(VT100);
1665 int i = def(esc_args[0], 0);
1666 if (i == 0 || i == 1) {
1667 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1669 ldisc_send(buf, 20);
1673 case ANSI('L', '='):
1674 compatibility(OTHER);
1675 use_bce = (esc_args[0] <= 0);
1676 erase_char = ERASE_CHAR;
1681 (ATTR_FGMASK | ATTR_BGMASK)));
1683 case ANSI('E', '='):
1684 compatibility(OTHER);
1685 blink_is_real = (esc_args[0] >= 1);
1687 case ANSI('p', '"'):
1688 /* Allow the host to make this emulator a 'perfect' VT102.
1689 * This first appeared in the VT220, but we do need to get
1690 * back to PuTTY mode so I won't check it.
1692 * The arg in 40..42 are a PuTTY extension.
1693 * The 2nd arg, 8bit vs 7bit is not checked.
1695 * Setting VT102 mode should also change the Fkeys to
1696 * generate PF* codes as a real VT102 has no Fkeys.
1697 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1700 * Note ESC c will NOT change this!
1703 switch (esc_args[0]) {
1705 compatibility_level &= ~TM_VTXXX;
1706 compatibility_level |= TM_VT102;
1709 compatibility_level &= ~TM_VTXXX;
1710 compatibility_level |= TM_VT220;
1714 if (esc_args[0] > 60 && esc_args[0] < 70)
1715 compatibility_level |= TM_VTXXX;
1719 compatibility_level &= TM_VTXXX;
1722 compatibility_level = TM_PUTTY;
1725 compatibility_level = TM_SCOANSI;
1729 compatibility_level = TM_PUTTY;
1735 /* Change the response to CSI c */
1736 if (esc_args[0] == 50) {
1739 strcpy(id_string, "\033[?");
1740 for (i = 1; i < esc_nargs; i++) {
1742 strcat(id_string, ";");
1743 sprintf(lbuf, "%d", esc_args[i]);
1744 strcat(id_string, lbuf);
1746 strcat(id_string, "c");
1749 /* Is this a good idea ?
1750 * Well we should do a soft reset at this point ...
1752 if (!has_compat(VT420) && has_compat(VT100)) {
1754 request_resize(132, 24, 1);
1756 request_resize(80, 24, 1);
1764 /* VT100 only here, checked above */
1767 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1770 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1773 default: /* specifically, 'B' */
1774 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1777 if (!has_compat(VT220) || c != '%')
1778 termstate = TOPLEVEL;
1783 case 'P': /* Linux palette sequence */
1784 termstate = SEEN_OSC_P;
1787 case 'R': /* Linux palette reset */
1790 termstate = TOPLEVEL;
1792 case 'W': /* word-set */
1793 termstate = SEEN_OSC_W;
1806 esc_args[0] = 10 * esc_args[0] + c - '0';
1810 * Grotty hack to support xterm and DECterm title
1811 * sequences concurrently.
1813 if (esc_args[0] == 2) {
1817 /* else fall through */
1819 termstate = OSC_STRING;
1825 * This OSC stuff is EVIL. It takes just one character to get into
1826 * sysline mode and it's not initially obvious how to get out.
1827 * So I've added CR and LF as string aborts.
1828 * This shouldn't effect compatibility as I believe embedded
1829 * control characters are supposed to be interpreted (maybe?)
1830 * and they don't display anything useful anyway.
1834 if (c == '\n' || c == '\r') {
1835 termstate = TOPLEVEL;
1836 } else if (c == 0234 || c == '\007') {
1838 * These characters terminate the string; ST and BEL
1839 * terminate the sequence and trigger instant
1840 * processing of it, whereas ESC goes back to SEEN_ESC
1841 * mode unless it is followed by \, in which case it is
1842 * synonymous with ST in the first place.
1845 termstate = TOPLEVEL;
1846 } else if (c == '\033')
1847 termstate = OSC_MAYBE_ST;
1848 else if (osc_strlen < OSC_STR_MAX)
1849 osc_string[osc_strlen++] = c;
1853 int max = (osc_strlen == 0 ? 21 : 16);
1855 if (c >= '0' && c <= '9')
1857 else if (c >= 'A' && c <= 'A' + max - 10)
1859 else if (c >= 'a' && c <= 'a' + max - 10)
1862 termstate = TOPLEVEL;
1863 osc_string[osc_strlen++] = val;
1864 if (osc_strlen >= 7) {
1865 palette_set(osc_string[0],
1866 osc_string[1] * 16 + osc_string[2],
1867 osc_string[3] * 16 + osc_string[4],
1868 osc_string[5] * 16 + osc_string[6]);
1870 termstate = TOPLEVEL;
1886 esc_args[0] = 10 * esc_args[0] + c - '0';
1889 termstate = OSC_STRING;
1895 unsigned long nlattr;
1896 unsigned long *ldata;
1902 for (i = 0; i < rows; i++) {
1904 for (j = 0; j < cols; j++)
1905 ldata[j] = ATTR_DEFAULT | 'E';
1909 seen_disp_event = TRUE;
1910 scrtop.x = scrtop.y = 0;
1913 check_selection(scrtop, scrbot);
1928 nlattr = LATTR_NORM;
1931 nlattr = LATTR_WIDE;
1935 ldata = lineptr(curs.y);
1936 ldata[cols] &= ~LATTR_MODE;
1937 ldata[cols] |= nlattr;
1940 termstate = TOPLEVEL;
1943 termstate = TOPLEVEL;
1944 seen_disp_event = TRUE;
1947 move(curs.x, curs.y - 1, 1);
1950 move(curs.x, curs.y + 1, 1);
1953 move(curs.x + 1, curs.y, 1);
1956 move(curs.x - 1, curs.y, 1);
1959 cset_attr[cset = 0] = ATTR_LINEDRW;
1962 cset_attr[cset = 0] = ATTR_ASCII;
1969 scroll(0, rows - 1, -1, TRUE);
1970 else if (curs.y > 0)
1976 erase_lots(FALSE, FALSE, TRUE);
1980 erase_lots(TRUE, FALSE, TRUE);
1983 /* XXX Print cursor line */
1986 /* XXX Start controller mode */
1989 /* XXX Stop controller mode */
1992 termstate = VT52_Y1;
1995 ldisc_send("\033/Z", 3);
1998 app_keypad_keys = TRUE;
2001 app_keypad_keys = FALSE;
2004 /* XXX This should switch to VT100 mode not current or default
2005 * VT mode. But this will only have effect in a VT220+
2011 /* XXX Enter auto print mode */
2014 /* XXX Exit auto print mode */
2017 /* XXX Print screen */
2022 termstate = VT52_Y2;
2023 move(curs.x, c - ' ', 0);
2026 termstate = TOPLEVEL;
2027 move(c - ' ', curs.y, 0);
2030 if (selstate != NO_SELECTION) {
2031 pos cursplus = curs;
2033 check_selection(curs, cursplus);
2040 * Compare two lines to determine whether they are sufficiently
2041 * alike to scroll-optimise one to the other. Return the degree of
2044 static int linecmp(unsigned long *a, unsigned long *b)
2048 for (i = n = 0; i < cols; i++)
2049 n += (*a++ == *b++);
2054 * Given a context, update the window. Out of paranoia, we don't
2055 * allow WM_PAINT responses to do scrolling optimisations.
2057 static void do_paint(Context ctx, int may_optimise)
2059 int i, j, start, our_curs_y;
2060 unsigned long attr, rv, cursor;
2066 * Check the visual bell state.
2069 ticks = GetTickCount();
2070 if (ticks - vbell_timeout >= 0)
2075 * screen array, disptop, scrtop,
2077 * cfg.blinkpc, blink_is_real, tblinker,
2078 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
2082 if (blinker || !cfg.blink_cur)
2083 cursor = ATTR_ACTCURS;
2087 cursor = ATTR_PASCURS;
2089 cursor |= ATTR_RIGHTCURS;
2092 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2093 our_curs_y = curs.y - disptop;
2095 for (i = 0; i < rows; i++) {
2096 unsigned long *ldata;
2098 scrpos.y = i + disptop;
2099 ldata = lineptr(scrpos.y);
2100 lattr = (ldata[cols] & LATTR_MODE);
2101 for (j = 0; j <= cols; j++) {
2102 unsigned long d = ldata[j];
2103 int idx = i * (cols + 1) + j;
2106 wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
2107 ^ (posle(selstart, scrpos) &&
2108 poslt(scrpos, selend) ?
2109 ATTR_REVERSE : 0)) |
2111 && j == curs.x ? cursor : 0));
2112 if (blink_is_real) {
2113 if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
2114 wanttext[idx] &= ATTR_MASK;
2115 wanttext[idx] += ' ';
2117 wanttext[idx] &= ~ATTR_BLINK;
2123 * We would perform scrolling optimisations in here, if they
2124 * didn't have a nasty tendency to cause the whole sodding
2125 * program to hang for a second at speed-critical moments.
2126 * We'll leave it well alone...
2129 for (i = 0; i < rows; i++) {
2130 int idx = i * (cols + 1);
2131 int lattr = (wanttext[idx + cols] & LATTR_MODE);
2133 for (j = 0; j <= cols; j++, idx++) {
2134 unsigned long t = wanttext[idx];
2135 int needs_update = (j < cols && t != disptext[idx]);
2136 int keep_going = (start != -1 && needs_update &&
2137 (t & ATTR_MASK) == attr &&
2138 j - start < sizeof(ch));
2139 if (start != -1 && !keep_going) {
2140 do_text(ctx, start, i, ch, j - start, attr, lattr);
2146 attr = t & ATTR_MASK;
2148 ch[j - start] = (char) (t & CHAR_MASK);
2156 * Flick the switch that says if blinking things should be shown or hidden.
2159 void term_blink(int flg)
2161 static long last_blink = 0;
2162 static long last_tblink = 0;
2163 long now, blink_diff;
2165 now = GetTickCount();
2166 blink_diff = now - last_tblink;
2168 /* Make sure the text blinks no more than 2Hz */
2169 if (blink_diff < 0 || blink_diff > 450) {
2171 tblinker = !tblinker;
2180 blink_diff = now - last_blink;
2182 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2183 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2191 * Invalidate the whole screen so it will be repainted in full.
2193 void term_invalidate(void)
2197 for (i = 0; i < rows * (cols + 1); i++)
2198 disptext[i] = ATTR_INVALID;
2202 * Paint the window in response to a WM_PAINT message.
2204 void term_paint(Context ctx, int l, int t, int r, int b)
2206 int i, j, left, top, right, bottom;
2208 left = l / font_width;
2209 right = (r - 1) / font_width;
2210 top = t / font_height;
2211 bottom = (b - 1) / font_height;
2212 for (i = top; i <= bottom && i < rows; i++) {
2213 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2214 for (j = left; j <= right && j < cols; j++)
2215 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2217 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2218 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2221 /* This should happen soon enough, also for some reason it sometimes
2222 * fails to actually do anything when re-sizing ... painting the wrong
2224 do_paint (ctx, FALSE);
2229 * Attempt to scroll the scrollback. The second parameter gives the
2230 * position we want to scroll to; the first is +1 to denote that
2231 * this position is relative to the beginning of the scrollback, -1
2232 * to denote it is relative to the end, and 0 to denote that it is
2233 * relative to the current position.
2235 void term_scroll(int rel, int where)
2237 int sbtop = -count234(scrollback);
2239 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2240 if (disptop < sbtop)
2248 static void clipme(pos top, pos bottom, char *workbuf)
2250 char *wbptr; /* where next char goes within workbuf */
2251 int wblen = 0; /* workbuf len */
2252 int buflen; /* amount of memory allocated to workbuf */
2254 if (workbuf != NULL) { /* user supplied buffer? */
2255 buflen = -1; /* assume buffer passed in is big enough */
2256 wbptr = workbuf; /* start filling here */
2258 buflen = 0; /* No data is available yet */
2260 while (poslt(top, bottom)) {
2262 unsigned long *ldata = lineptr(top.y);
2268 if (!(ldata[cols] & ATTR_WRAPPED)) {
2269 while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
2270 && poslt(top, nlpos)) decpos(nlpos);
2271 if (poslt(nlpos, bottom))
2274 while (poslt(top, bottom) && poslt(top, nlpos)) {
2275 int ch = (ldata[top.x] & CHAR_MASK);
2276 int set = (ldata[top.x] & CSET_MASK);
2278 /* VT Specials -> ISO8859-1 for Cut&Paste */
2279 static const unsigned char poorman2[] =
2280 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2282 if (set && !cfg.rawcnp) {
2283 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2285 if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
2287 ch = (x << 8) + poorman2[2 * (ch - 0x60)];
2292 if (cfg.rawcnp || !!(ch & 0xE0)) {
2293 if (wblen == buflen) {
2294 workbuf = srealloc(workbuf, buflen += 100);
2295 wbptr = workbuf + wblen;
2298 *wbptr++ = (unsigned char) ch;
2306 for (i = 0; i < sizeof(sel_nl); i++) {
2307 if (wblen == buflen) {
2308 workbuf = srealloc(workbuf, buflen += 100);
2309 wbptr = workbuf + wblen;
2312 *wbptr++ = sel_nl[i];
2318 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2319 if (buflen > 0) /* indicates we allocated this buffer */
2323 void term_copyall(void)
2326 top.y = -count234(scrollback);
2328 clipme(top, curs, NULL /* dynamic allocation */ );
2332 * Spread the selection outwards according to the selection mode.
2334 static pos sel_spread_half(pos p, int dir)
2336 unsigned long *ldata;
2339 ldata = lineptr(p.y);
2344 * In this mode, every character is a separate unit, except
2345 * for runs of spaces at the end of a non-wrapping line.
2347 if (!(ldata[cols] & ATTR_WRAPPED)) {
2348 unsigned long *q = ldata + cols;
2349 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2351 if (q == ldata + cols)
2353 if (p.x >= q - ldata)
2354 p.x = (dir == -1 ? q - ldata : cols - 1);
2359 * In this mode, the units are maximal runs of characters
2360 * whose `wordness' has the same value.
2362 wvalue = wordness[ldata[p.x] & CHAR_MASK];
2365 && wordness[ldata[p.x + 1] & CHAR_MASK] ==
2369 && wordness[ldata[p.x - 1] & CHAR_MASK] ==
2375 * In this mode, every line is a unit.
2377 p.x = (dir == -1 ? 0 : cols - 1);
2383 static void sel_spread(void)
2385 selstart = sel_spread_half(selstart, -1);
2387 selend = sel_spread_half(selend, +1);
2391 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2392 int shift, int ctrl)
2395 unsigned long *ldata;
2411 selpoint.y = y + disptop;
2413 ldata = lineptr(selpoint.y);
2414 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2418 int encstate = 0, r, c;
2420 static int is_down = 0;
2424 encstate = 0x20; /* left button down */
2435 case MBT_WHEEL_DOWN:
2441 if (xterm_mouse == 1)
2462 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
2463 ldisc_send(abuf, 6);
2467 b = translate_button(b);
2469 if (b == MBT_SELECT && a == MA_CLICK) {
2471 selstate = ABOUT_TO;
2472 selanchor = selpoint;
2474 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2476 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2477 selstate = DRAGGING;
2478 selstart = selanchor = selpoint;
2482 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
2483 (b == MBT_EXTEND && a != MA_RELEASE)) {
2484 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2486 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2487 if (posdiff(selpoint, selstart) <
2488 posdiff(selend, selstart) / 2) {
2492 selanchor = selstart;
2494 selstate = DRAGGING;
2496 if (selstate != ABOUT_TO && selstate != DRAGGING)
2497 selanchor = selpoint;
2498 selstate = DRAGGING;
2499 if (poslt(selpoint, selanchor)) {
2500 selstart = selpoint;
2504 selstart = selanchor;
2509 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
2510 if (selstate == DRAGGING) {
2512 * We've completed a selection. We now transfer the
2513 * data to the clipboard.
2515 clipme(selstart, selend, selspace);
2516 selstate = SELECTED;
2518 selstate = NO_SELECTION;
2519 } else if (b == MBT_PASTE
2520 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
2524 get_clip((void **) &data, &len);
2529 sfree(paste_buffer);
2530 paste_pos = paste_hold = paste_len = 0;
2531 paste_buffer = smalloc(len);
2534 while (p < data + len) {
2535 while (p < data + len &&
2536 !(p <= data + len - sizeof(sel_nl) &&
2537 !memcmp(p, sel_nl, sizeof(sel_nl))))
2543 for (i = 0; i < p - q; i++) {
2544 c = xlat_kbd2tty(q[i]);
2545 paste_buffer[paste_len++] = c;
2549 if (p <= data + len - sizeof(sel_nl) &&
2550 !memcmp(p, sel_nl, sizeof(sel_nl))) {
2551 paste_buffer[paste_len++] = '\r';
2552 p += sizeof(sel_nl);
2557 /* Assume a small paste will be OK in one go. */
2558 if (paste_len < 256) {
2559 ldisc_send(paste_buffer, paste_len);
2561 sfree(paste_buffer);
2563 paste_pos = paste_hold = paste_len = 0;
2566 get_clip(NULL, NULL);
2576 sfree(paste_buffer);
2583 static long last_paste = 0;
2584 long now, paste_diff;
2589 /* Don't wait forever to paste */
2591 now = GetTickCount();
2592 paste_diff = now - last_paste;
2593 if (paste_diff >= 0 && paste_diff < 450)
2598 while (paste_pos < paste_len) {
2600 while (n + paste_pos < paste_len) {
2601 if (paste_buffer[paste_pos + n++] == '\r')
2604 ldisc_send(paste_buffer + paste_pos, n);
2607 if (paste_pos < paste_len) {
2612 sfree(paste_buffer);
2617 static void deselect(void)
2619 selstate = NO_SELECTION;
2620 selstart.x = selstart.y = selend.x = selend.y = 0;
2623 void term_deselect(void)
2629 int term_ldisc(int option)
2631 if (option == LD_ECHO)
2632 return term_echoing;
2633 if (option == LD_EDIT)
2634 return term_editing;
2639 * from_backend(), to get data from the backend for the terminal.
2641 void from_backend(int is_stderr, char *data, int len)
2644 if (inbuf_head >= INBUF_SIZE)
2646 inbuf[inbuf_head++] = *data++;
2651 * Log session traffic.
2653 void logtraffic(unsigned char c, int logmode)
2655 if (cfg.logtype > 0) {
2656 if (cfg.logtype == logmode) {
2657 /* deferred open file from pgm start? */
2666 /* open log file append/overwrite mode */
2676 sprintf(writemod, "wb"); /* default to rewrite */
2677 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
2681 i = askappend(cfg.logfilename);
2683 writemod[0] = 'a'; /* set append mode */
2684 else if (i == 0) { /* cancelled */
2686 cfg.logtype = 0; /* disable logging */
2691 lgfp = fopen(cfg.logfilename, writemod);
2692 if (lgfp) { /* enter into event log */
2693 sprintf(buf, "%s session log (%s mode) to file : ",
2694 (writemod[0] == 'a') ? "Appending" : "Writing new",
2695 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2696 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
2697 /* Make sure we do not exceed the output buffer size */
2698 strncat(buf, cfg.logfilename, 128);
2699 buf[strlen(buf)] = '\0';
2702 /* --- write header line iinto log file */
2703 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2706 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2708 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2712 void logfclose(void)