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);
1060 if (has_compat(SCOANSI)) {
1062 erase_lots(FALSE, FALSE, TRUE);
1065 seen_disp_event = 1;
1069 compatibility(VT100);
1071 if (curs.y == marg_b)
1072 scroll(marg_t, marg_b, 1, TRUE);
1073 else if (curs.y < rows - 1)
1079 seen_disp_event = 1;
1081 logtraffic((unsigned char) c, LGTYP_ASCII);
1085 pos old_curs = curs;
1086 unsigned long *ldata = lineptr(curs.y);
1090 } while (curs.x < cols - 1 && !tabs[curs.x]);
1092 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1093 if (curs.x >= cols / 2)
1094 curs.x = cols / 2 - 1;
1101 check_selection(old_curs, curs);
1103 seen_disp_event = TRUE;
1105 case '\177': /* Destructive backspace
1106 This does nothing on a real VT100 */
1107 compatibility(OTHER);
1108 if (curs.x && !wrapnext)
1112 *cpos = (' ' | curr_attr | ATTR_ASCII);
1116 switch (termstate) {
1118 /* Only graphic characters get this far, ctrls are stripped above */
1119 if (wrapnext && wrap) {
1120 cpos[1] |= ATTR_WRAPPED;
1121 if (curs.y == marg_b)
1122 scroll(marg_t, marg_b, 1, TRUE);
1123 else if (curs.y < rows - 1)
1131 if (selstate != NO_SELECTION) {
1132 pos cursplus = curs;
1134 check_selection(curs, cursplus);
1136 switch (cset_attr[cset]) {
1138 * Linedraw characters are different from 'ESC ( B'
1139 * only for a small range. For ones outside that
1140 * range, make sure we use the same font as well as
1141 * the same encoding.
1144 if (c < 0x5f || c > 0x7F)
1146 xlat_tty2scr((unsigned char) c) | curr_attr |
1149 *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1152 ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
1155 /* If UK-ASCII, make the '#' a LineDraw Pound */
1157 *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1160 /*FALLTHROUGH*/ default:
1161 *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
1162 (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1163 logtraffic((unsigned char) c, LGTYP_ASCII);
1168 if (curs.x == cols) {
1173 seen_disp_event = 1;
1177 termstate = TOPLEVEL;
1181 * This state is virtually identical to SEEN_ESC, with the
1182 * exception that we have an OSC sequence in the pipeline,
1183 * and _if_ we see a backslash, we process it.
1187 termstate = TOPLEVEL;
1190 /* else fall through */
1192 termstate = TOPLEVEL;
1194 case ' ': /* some weird sequence? */
1195 compatibility(VT220);
1196 termstate = IGNORE_NEXT;
1198 case '[': /* enter CSI mode */
1199 termstate = SEEN_CSI;
1201 esc_args[0] = ARG_DEFAULT;
1204 case ']': /* xterm escape sequences */
1205 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1206 compatibility(OTHER);
1207 termstate = SEEN_OSC;
1210 case '(': /* should set GL */
1211 compatibility(VT100);
1214 case ')': /* should set GR */
1215 compatibility(VT100);
1218 case '7': /* save cursor */
1219 compatibility(VT100);
1222 case '8': /* restore cursor */
1223 compatibility(VT100);
1225 seen_disp_event = TRUE;
1228 compatibility(VT100);
1229 app_keypad_keys = TRUE;
1232 compatibility(VT100);
1233 app_keypad_keys = FALSE;
1235 case 'D': /* exactly equivalent to LF */
1236 compatibility(VT100);
1237 if (curs.y == marg_b)
1238 scroll(marg_t, marg_b, 1, TRUE);
1239 else if (curs.y < rows - 1)
1243 seen_disp_event = TRUE;
1245 case 'E': /* exactly equivalent to CR-LF */
1246 compatibility(VT100);
1248 if (curs.y == marg_b)
1249 scroll(marg_t, marg_b, 1, TRUE);
1250 else if (curs.y < rows - 1)
1254 seen_disp_event = TRUE;
1256 case 'M': /* reverse index - backwards LF */
1257 compatibility(VT100);
1258 if (curs.y == marg_t)
1259 scroll(marg_t, marg_b, -1, TRUE);
1260 else if (curs.y > 0)
1264 seen_disp_event = TRUE;
1266 case 'Z': /* terminal type query */
1267 compatibility(VT100);
1268 ldisc_send(id_string, strlen(id_string));
1270 case 'c': /* restore power-on settings */
1271 compatibility(VT100);
1274 request_resize(80, rows, 1);
1279 seen_disp_event = TRUE;
1281 case '#': /* ESC # 8 fills screen with Es :-) */
1282 compatibility(VT100);
1283 termstate = SEEN_ESCHASH;
1285 case 'H': /* set a tab */
1286 compatibility(VT100);
1287 tabs[curs.x] = TRUE;
1292 termstate = TOPLEVEL; /* default */
1294 if (esc_nargs <= ARGS_MAX) {
1295 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1296 esc_args[esc_nargs - 1] = 0;
1297 esc_args[esc_nargs - 1] =
1298 10 * esc_args[esc_nargs - 1] + c - '0';
1300 termstate = SEEN_CSI;
1301 } else if (c == ';') {
1302 if (++esc_nargs <= ARGS_MAX)
1303 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1304 termstate = SEEN_CSI;
1305 } else if (c < '@') {
1312 termstate = SEEN_CSI;
1314 switch (ANSI(c, esc_query)) {
1315 case 'A': /* move up N lines */
1316 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1317 seen_disp_event = TRUE;
1319 case 'e': /* move down N lines */
1320 compatibility(ANSI);
1322 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1323 seen_disp_event = TRUE;
1325 case 'a': /* move right N cols */
1326 compatibility(ANSI);
1327 case ANSI('c', '>'): /* report xterm version */
1328 compatibility(OTHER);
1329 /* this reports xterm version 136 so that VIM can
1330 use the drag messages from the mouse reporting */
1331 ldisc_send("\033[>0;136;0c", 11);
1334 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1335 seen_disp_event = TRUE;
1337 case 'D': /* move left N cols */
1338 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1339 seen_disp_event = TRUE;
1341 case 'E': /* move down N lines and CR */
1342 compatibility(ANSI);
1343 move(0, curs.y + def(esc_args[0], 1), 1);
1344 seen_disp_event = TRUE;
1346 case 'F': /* move up N lines and CR */
1347 compatibility(ANSI);
1348 move(0, curs.y - def(esc_args[0], 1), 1);
1349 seen_disp_event = TRUE;
1352 case '`': /* set horizontal posn */
1353 compatibility(ANSI);
1354 move(def(esc_args[0], 1) - 1, curs.y, 0);
1355 seen_disp_event = TRUE;
1357 case 'd': /* set vertical posn */
1358 compatibility(ANSI);
1360 (dec_om ? marg_t : 0) + def(esc_args[0],
1363 seen_disp_event = TRUE;
1366 case 'f': /* set horz and vert posns at once */
1368 esc_args[1] = ARG_DEFAULT;
1369 move(def(esc_args[1], 1) - 1,
1370 (dec_om ? marg_t : 0) + def(esc_args[0],
1373 seen_disp_event = TRUE;
1375 case 'J': /* erase screen or parts of it */
1377 unsigned int i = def(esc_args[0], 0) + 1;
1380 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1383 seen_disp_event = TRUE;
1385 case 'K': /* erase line or parts of it */
1387 unsigned int i = def(esc_args[0], 0) + 1;
1390 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1392 seen_disp_event = TRUE;
1394 case 'L': /* insert 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 'M': /* delete lines */
1403 compatibility(VT102);
1404 if (curs.y <= marg_b)
1405 scroll(curs.y, marg_b, def(esc_args[0], 1),
1408 seen_disp_event = TRUE;
1410 case '@': /* insert chars */
1411 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1412 compatibility(VT102);
1413 insch(def(esc_args[0], 1));
1414 seen_disp_event = TRUE;
1416 case 'P': /* delete chars */
1417 compatibility(VT102);
1418 insch(-def(esc_args[0], 1));
1419 seen_disp_event = TRUE;
1421 case 'c': /* terminal type query */
1422 compatibility(VT100);
1423 /* This is the response for a VT102 */
1424 ldisc_send(id_string, strlen(id_string));
1426 case 'n': /* cursor position query */
1427 if (esc_args[0] == 6) {
1429 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1431 ldisc_send(buf, strlen(buf));
1432 } else if (esc_args[0] == 5) {
1433 ldisc_send("\033[0n", 4);
1436 case 'h': /* toggle modes to high */
1438 compatibility(VT100);
1441 for (i = 0; i < esc_nargs; i++)
1442 toggle_mode(esc_args[i], esc_query, TRUE);
1445 case 'l': /* toggle modes to low */
1447 compatibility(VT100);
1450 for (i = 0; i < esc_nargs; i++)
1451 toggle_mode(esc_args[i], esc_query, FALSE);
1454 case 'g': /* clear tabs */
1455 compatibility(VT100);
1456 if (esc_nargs == 1) {
1457 if (esc_args[0] == 0) {
1458 tabs[curs.x] = FALSE;
1459 } else if (esc_args[0] == 3) {
1461 for (i = 0; i < cols; i++)
1466 case 'r': /* set scroll margins */
1467 compatibility(VT100);
1468 if (esc_nargs <= 2) {
1470 top = def(esc_args[0], 1) - 1;
1471 bot = (esc_nargs <= 1
1473 0 ? rows : def(esc_args[1], rows)) - 1;
1476 /* VTTEST Bug 9 - if region is less than 2 lines
1477 * don't change region.
1479 if (bot - top > 0) {
1484 * I used to think the cursor should be
1485 * placed at the top of the newly marginned
1486 * area. Apparently not: VMS TPU falls over
1489 * Well actually it should for Origin mode - RDB
1491 curs.y = (dec_om ? marg_t : 0);
1493 seen_disp_event = TRUE;
1497 case 'm': /* set graphics rendition */
1500 * A VT100 without the AVO only had one attribute, either
1501 * underline or reverse video depending on the cursor type,
1502 * this was selected by CSI 7m.
1505 * This is DIM on the VT100-AVO and VT102
1507 * This is BLINK on the VT100-AVO and VT102+
1509 * This is INVIS on the VT100-AVO and VT102
1511 * This like 22 disables BOLD, DIM and INVIS
1513 * The ANSI colours appear on any terminal that has colour
1514 * (obviously) but the interaction between sgr0 and the
1515 * colours varies but is usually related to the background
1516 * colour erase item.
1517 * The interaction between colour attributes and the mono
1518 * ones is also very implementation dependent.
1520 * The 39 and 49 attributes are likely to be unimplemented.
1523 for (i = 0; i < esc_nargs; i++) {
1524 switch (def(esc_args[i], 0)) {
1525 case 0: /* restore defaults */
1526 curr_attr = ATTR_DEFAULT;
1528 case 1: /* enable bold */
1529 compatibility(VT100AVO);
1530 curr_attr |= ATTR_BOLD;
1532 case 21: /* (enable double underline) */
1533 compatibility(OTHER);
1534 case 4: /* enable underline */
1535 compatibility(VT100AVO);
1536 curr_attr |= ATTR_UNDER;
1538 case 5: /* enable blink */
1539 compatibility(VT100AVO);
1540 curr_attr |= ATTR_BLINK;
1542 case 7: /* enable reverse video */
1543 curr_attr |= ATTR_REVERSE;
1545 case 22: /* disable bold */
1546 compatibility2(OTHER, VT220);
1547 curr_attr &= ~ATTR_BOLD;
1549 case 24: /* disable underline */
1550 compatibility2(OTHER, VT220);
1551 curr_attr &= ~ATTR_UNDER;
1553 case 25: /* disable blink */
1554 compatibility2(OTHER, VT220);
1555 curr_attr &= ~ATTR_BLINK;
1557 case 27: /* disable reverse video */
1558 compatibility2(OTHER, VT220);
1559 curr_attr &= ~ATTR_REVERSE;
1570 curr_attr &= ~ATTR_FGMASK;
1572 (esc_args[i] - 30) << ATTR_FGSHIFT;
1574 case 39: /* default-foreground */
1575 curr_attr &= ~ATTR_FGMASK;
1576 curr_attr |= ATTR_DEFFG;
1587 curr_attr &= ~ATTR_BGMASK;
1589 (esc_args[i] - 40) << ATTR_BGSHIFT;
1591 case 49: /* default-background */
1592 curr_attr &= ~ATTR_BGMASK;
1593 curr_attr |= ATTR_DEFBG;
1601 (ATTR_FGMASK | ATTR_BGMASK |
1605 case 's': /* save cursor */
1608 case 'u': /* restore cursor */
1610 seen_disp_event = TRUE;
1612 case 't': /* set page size - ie window height */
1614 * VT340/VT420 sequence DECSLPP, DEC only allows values
1615 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1616 * illegal values (eg first arg 1..9) for window changing
1619 compatibility(VT340TEXT);
1621 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1622 request_resize(cols, def(esc_args[0], 24), 0);
1627 compatibility(SCOANSI);
1628 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1631 seen_disp_event = TRUE;
1634 compatibility(SCOANSI);
1635 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1638 seen_disp_event = TRUE;
1640 case ANSI('|', '*'):
1641 /* VT420 sequence DECSNLS
1642 * Set number of lines on screen
1643 * VT420 uses VGA like hardware and can support any size in
1644 * reasonable range (24..49 AIUI) with no default specified.
1646 compatibility(VT420);
1647 if (esc_nargs == 1 && esc_args[0] > 0) {
1648 request_resize(cols,
1649 def(esc_args[0], cfg.height),
1654 case ANSI('|', '$'):
1655 /* VT340/VT420 sequence DECSCPP
1656 * Set number of columns per page
1657 * Docs imply range is only 80 or 132, but I'll allow any.
1659 compatibility(VT340TEXT);
1660 if (esc_nargs <= 1) {
1661 request_resize(def(esc_args[0], cfg.width),
1666 case 'X': /* write N spaces w/o moving cursor */
1667 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1668 compatibility(ANSIMIN);
1670 int n = def(esc_args[0], 1);
1672 unsigned long *p = cpos;
1673 if (n > cols - curs.x)
1677 check_selection(curs, cursplus);
1680 seen_disp_event = TRUE;
1683 case 'x': /* report terminal characteristics */
1684 compatibility(VT100);
1687 int i = def(esc_args[0], 0);
1688 if (i == 0 || i == 1) {
1689 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1691 ldisc_send(buf, 20);
1695 case ANSI('L', '='):
1696 compatibility(OTHER);
1697 use_bce = (esc_args[0] <= 0);
1698 erase_char = ERASE_CHAR;
1703 (ATTR_FGMASK | ATTR_BGMASK)));
1705 case ANSI('E', '='):
1706 compatibility(OTHER);
1707 blink_is_real = (esc_args[0] >= 1);
1709 case ANSI('p', '"'):
1710 /* Allow the host to make this emulator a 'perfect' VT102.
1711 * This first appeared in the VT220, but we do need to get
1712 * back to PuTTY mode so I won't check it.
1714 * The arg in 40..42 are a PuTTY extension.
1715 * The 2nd arg, 8bit vs 7bit is not checked.
1717 * Setting VT102 mode should also change the Fkeys to
1718 * generate PF* codes as a real VT102 has no Fkeys.
1719 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1722 * Note ESC c will NOT change this!
1725 switch (esc_args[0]) {
1727 compatibility_level &= ~TM_VTXXX;
1728 compatibility_level |= TM_VT102;
1731 compatibility_level &= ~TM_VTXXX;
1732 compatibility_level |= TM_VT220;
1736 if (esc_args[0] > 60 && esc_args[0] < 70)
1737 compatibility_level |= TM_VTXXX;
1741 compatibility_level &= TM_VTXXX;
1744 compatibility_level = TM_PUTTY;
1747 compatibility_level = TM_SCOANSI;
1751 compatibility_level = TM_PUTTY;
1757 /* Change the response to CSI c */
1758 if (esc_args[0] == 50) {
1761 strcpy(id_string, "\033[?");
1762 for (i = 1; i < esc_nargs; i++) {
1764 strcat(id_string, ";");
1765 sprintf(lbuf, "%d", esc_args[i]);
1766 strcat(id_string, lbuf);
1768 strcat(id_string, "c");
1771 /* Is this a good idea ?
1772 * Well we should do a soft reset at this point ...
1774 if (!has_compat(VT420) && has_compat(VT100)) {
1776 request_resize(132, 24, 1);
1778 request_resize(80, 24, 1);
1786 /* VT100 only here, checked above */
1789 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1792 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1795 default: /* specifically, 'B' */
1796 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1799 if (!has_compat(VT220) || c != '%')
1800 termstate = TOPLEVEL;
1805 case 'P': /* Linux palette sequence */
1806 termstate = SEEN_OSC_P;
1809 case 'R': /* Linux palette reset */
1812 termstate = TOPLEVEL;
1814 case 'W': /* word-set */
1815 termstate = SEEN_OSC_W;
1828 esc_args[0] = 10 * esc_args[0] + c - '0';
1832 * Grotty hack to support xterm and DECterm title
1833 * sequences concurrently.
1835 if (esc_args[0] == 2) {
1839 /* else fall through */
1841 termstate = OSC_STRING;
1847 * This OSC stuff is EVIL. It takes just one character to get into
1848 * sysline mode and it's not initially obvious how to get out.
1849 * So I've added CR and LF as string aborts.
1850 * This shouldn't effect compatibility as I believe embedded
1851 * control characters are supposed to be interpreted (maybe?)
1852 * and they don't display anything useful anyway.
1856 if (c == '\n' || c == '\r') {
1857 termstate = TOPLEVEL;
1858 } else if (c == 0234 || c == '\007') {
1860 * These characters terminate the string; ST and BEL
1861 * terminate the sequence and trigger instant
1862 * processing of it, whereas ESC goes back to SEEN_ESC
1863 * mode unless it is followed by \, in which case it is
1864 * synonymous with ST in the first place.
1867 termstate = TOPLEVEL;
1868 } else if (c == '\033')
1869 termstate = OSC_MAYBE_ST;
1870 else if (osc_strlen < OSC_STR_MAX)
1871 osc_string[osc_strlen++] = c;
1875 int max = (osc_strlen == 0 ? 21 : 16);
1877 if (c >= '0' && c <= '9')
1879 else if (c >= 'A' && c <= 'A' + max - 10)
1881 else if (c >= 'a' && c <= 'a' + max - 10)
1884 termstate = TOPLEVEL;
1885 osc_string[osc_strlen++] = val;
1886 if (osc_strlen >= 7) {
1887 palette_set(osc_string[0],
1888 osc_string[1] * 16 + osc_string[2],
1889 osc_string[3] * 16 + osc_string[4],
1890 osc_string[5] * 16 + osc_string[6]);
1892 termstate = TOPLEVEL;
1908 esc_args[0] = 10 * esc_args[0] + c - '0';
1911 termstate = OSC_STRING;
1917 unsigned long nlattr;
1918 unsigned long *ldata;
1924 for (i = 0; i < rows; i++) {
1926 for (j = 0; j < cols; j++)
1927 ldata[j] = ATTR_DEFAULT | 'E';
1931 seen_disp_event = TRUE;
1932 scrtop.x = scrtop.y = 0;
1935 check_selection(scrtop, scrbot);
1950 nlattr = LATTR_NORM;
1953 nlattr = LATTR_WIDE;
1957 ldata = lineptr(curs.y);
1958 ldata[cols] &= ~LATTR_MODE;
1959 ldata[cols] |= nlattr;
1962 termstate = TOPLEVEL;
1965 termstate = TOPLEVEL;
1966 seen_disp_event = TRUE;
1969 move(curs.x, curs.y - 1, 1);
1972 move(curs.x, curs.y + 1, 1);
1975 move(curs.x + 1, curs.y, 1);
1978 move(curs.x - 1, curs.y, 1);
1981 cset_attr[cset = 0] = ATTR_LINEDRW;
1984 cset_attr[cset = 0] = ATTR_ASCII;
1991 scroll(0, rows - 1, -1, TRUE);
1992 else if (curs.y > 0)
1998 erase_lots(FALSE, FALSE, TRUE);
2002 erase_lots(TRUE, FALSE, TRUE);
2005 /* XXX Print cursor line */
2008 /* XXX Start controller mode */
2011 /* XXX Stop controller mode */
2014 termstate = VT52_Y1;
2017 ldisc_send("\033/Z", 3);
2020 app_keypad_keys = TRUE;
2023 app_keypad_keys = FALSE;
2026 /* XXX This should switch to VT100 mode not current or default
2027 * VT mode. But this will only have effect in a VT220+
2033 /* XXX Enter auto print mode */
2036 /* XXX Exit auto print mode */
2039 /* XXX Print screen */
2044 termstate = VT52_Y2;
2045 move(curs.x, c - ' ', 0);
2048 termstate = TOPLEVEL;
2049 move(c - ' ', curs.y, 0);
2052 if (selstate != NO_SELECTION) {
2053 pos cursplus = curs;
2055 check_selection(curs, cursplus);
2062 * Compare two lines to determine whether they are sufficiently
2063 * alike to scroll-optimise one to the other. Return the degree of
2066 static int linecmp(unsigned long *a, unsigned long *b)
2070 for (i = n = 0; i < cols; i++)
2071 n += (*a++ == *b++);
2076 * Given a context, update the window. Out of paranoia, we don't
2077 * allow WM_PAINT responses to do scrolling optimisations.
2079 static void do_paint(Context ctx, int may_optimise)
2081 int i, j, start, our_curs_y;
2082 unsigned long attr, rv, cursor;
2088 * Check the visual bell state.
2091 ticks = GetTickCount();
2092 if (ticks - vbell_timeout >= 0)
2097 * screen array, disptop, scrtop,
2099 * cfg.blinkpc, blink_is_real, tblinker,
2100 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
2104 if (blinker || !cfg.blink_cur)
2105 cursor = ATTR_ACTCURS;
2109 cursor = ATTR_PASCURS;
2111 cursor |= ATTR_RIGHTCURS;
2114 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2115 our_curs_y = curs.y - disptop;
2117 for (i = 0; i < rows; i++) {
2118 unsigned long *ldata;
2120 scrpos.y = i + disptop;
2121 ldata = lineptr(scrpos.y);
2122 lattr = (ldata[cols] & LATTR_MODE);
2123 for (j = 0; j <= cols; j++) {
2124 unsigned long d = ldata[j];
2125 int idx = i * (cols + 1) + j;
2128 wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
2129 ^ (posle(selstart, scrpos) &&
2130 poslt(scrpos, selend) ?
2131 ATTR_REVERSE : 0)) |
2133 && j == curs.x ? cursor : 0));
2134 if (blink_is_real) {
2135 if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
2136 wanttext[idx] &= ATTR_MASK;
2137 wanttext[idx] += ' ';
2139 wanttext[idx] &= ~ATTR_BLINK;
2145 * We would perform scrolling optimisations in here, if they
2146 * didn't have a nasty tendency to cause the whole sodding
2147 * program to hang for a second at speed-critical moments.
2148 * We'll leave it well alone...
2151 for (i = 0; i < rows; i++) {
2152 int idx = i * (cols + 1);
2153 int lattr = (wanttext[idx + cols] & LATTR_MODE);
2155 for (j = 0; j <= cols; j++, idx++) {
2156 unsigned long t = wanttext[idx];
2157 int needs_update = (j < cols && t != disptext[idx]);
2158 int keep_going = (start != -1 && needs_update &&
2159 (t & ATTR_MASK) == attr &&
2160 j - start < sizeof(ch));
2161 if (start != -1 && !keep_going) {
2162 do_text(ctx, start, i, ch, j - start, attr, lattr);
2168 attr = t & ATTR_MASK;
2170 ch[j - start] = (char) (t & CHAR_MASK);
2178 * Flick the switch that says if blinking things should be shown or hidden.
2181 void term_blink(int flg)
2183 static long last_blink = 0;
2184 static long last_tblink = 0;
2185 long now, blink_diff;
2187 now = GetTickCount();
2188 blink_diff = now - last_tblink;
2190 /* Make sure the text blinks no more than 2Hz */
2191 if (blink_diff < 0 || blink_diff > 450) {
2193 tblinker = !tblinker;
2202 blink_diff = now - last_blink;
2204 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2205 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2213 * Invalidate the whole screen so it will be repainted in full.
2215 void term_invalidate(void)
2219 for (i = 0; i < rows * (cols + 1); i++)
2220 disptext[i] = ATTR_INVALID;
2224 * Paint the window in response to a WM_PAINT message.
2226 void term_paint(Context ctx, int l, int t, int r, int b)
2228 int i, j, left, top, right, bottom;
2230 left = l / font_width;
2231 right = (r - 1) / font_width;
2232 top = t / font_height;
2233 bottom = (b - 1) / font_height;
2234 for (i = top; i <= bottom && i < rows; i++) {
2235 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2236 for (j = left; j <= right && j < cols; j++)
2237 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2239 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2240 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2243 /* This should happen soon enough, also for some reason it sometimes
2244 * fails to actually do anything when re-sizing ... painting the wrong
2246 do_paint (ctx, FALSE);
2251 * Attempt to scroll the scrollback. The second parameter gives the
2252 * position we want to scroll to; the first is +1 to denote that
2253 * this position is relative to the beginning of the scrollback, -1
2254 * to denote it is relative to the end, and 0 to denote that it is
2255 * relative to the current position.
2257 void term_scroll(int rel, int where)
2259 int sbtop = -count234(scrollback);
2261 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2262 if (disptop < sbtop)
2270 static void clipme(pos top, pos bottom, char *workbuf)
2272 char *wbptr; /* where next char goes within workbuf */
2273 int wblen = 0; /* workbuf len */
2274 int buflen; /* amount of memory allocated to workbuf */
2276 if (workbuf != NULL) { /* user supplied buffer? */
2277 buflen = -1; /* assume buffer passed in is big enough */
2278 wbptr = workbuf; /* start filling here */
2280 buflen = 0; /* No data is available yet */
2282 while (poslt(top, bottom)) {
2284 unsigned long *ldata = lineptr(top.y);
2290 if (!(ldata[cols] & ATTR_WRAPPED)) {
2291 while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
2292 && poslt(top, nlpos)) decpos(nlpos);
2293 if (poslt(nlpos, bottom))
2296 while (poslt(top, bottom) && poslt(top, nlpos)) {
2297 int ch = (ldata[top.x] & CHAR_MASK);
2298 int set = (ldata[top.x] & CSET_MASK);
2300 /* VT Specials -> ISO8859-1 for Cut&Paste */
2301 static const unsigned char poorman2[] =
2302 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2304 if (set && !cfg.rawcnp) {
2305 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2307 if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
2309 ch = (x << 8) + poorman2[2 * (ch - 0x60)];
2314 if (cfg.rawcnp || !!(ch & 0xE0)) {
2315 if (wblen == buflen) {
2316 workbuf = srealloc(workbuf, buflen += 100);
2317 wbptr = workbuf + wblen;
2320 *wbptr++ = (unsigned char) ch;
2328 for (i = 0; i < sizeof(sel_nl); i++) {
2329 if (wblen == buflen) {
2330 workbuf = srealloc(workbuf, buflen += 100);
2331 wbptr = workbuf + wblen;
2334 *wbptr++ = sel_nl[i];
2340 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2341 if (buflen > 0) /* indicates we allocated this buffer */
2345 void term_copyall(void)
2348 top.y = -count234(scrollback);
2350 clipme(top, curs, NULL /* dynamic allocation */ );
2354 * Spread the selection outwards according to the selection mode.
2356 static pos sel_spread_half(pos p, int dir)
2358 unsigned long *ldata;
2361 ldata = lineptr(p.y);
2366 * In this mode, every character is a separate unit, except
2367 * for runs of spaces at the end of a non-wrapping line.
2369 if (!(ldata[cols] & ATTR_WRAPPED)) {
2370 unsigned long *q = ldata + cols;
2371 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2373 if (q == ldata + cols)
2375 if (p.x >= q - ldata)
2376 p.x = (dir == -1 ? q - ldata : cols - 1);
2381 * In this mode, the units are maximal runs of characters
2382 * whose `wordness' has the same value.
2384 wvalue = wordness[ldata[p.x] & CHAR_MASK];
2387 && wordness[ldata[p.x + 1] & CHAR_MASK] ==
2391 && wordness[ldata[p.x - 1] & CHAR_MASK] ==
2397 * In this mode, every line is a unit.
2399 p.x = (dir == -1 ? 0 : cols - 1);
2405 static void sel_spread(void)
2407 selstart = sel_spread_half(selstart, -1);
2409 selend = sel_spread_half(selend, +1);
2413 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2414 int shift, int ctrl)
2417 unsigned long *ldata;
2433 selpoint.y = y + disptop;
2435 ldata = lineptr(selpoint.y);
2436 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2440 int encstate = 0, r, c;
2442 static int is_down = 0;
2446 encstate = 0x20; /* left button down */
2457 case MBT_WHEEL_DOWN:
2463 if (xterm_mouse == 1)
2484 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
2485 ldisc_send(abuf, 6);
2489 b = translate_button(b);
2491 if (b == MBT_SELECT && a == MA_CLICK) {
2493 selstate = ABOUT_TO;
2494 selanchor = selpoint;
2496 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2498 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2499 selstate = DRAGGING;
2500 selstart = selanchor = selpoint;
2504 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
2505 (b == MBT_EXTEND && a != MA_RELEASE)) {
2506 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2508 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2509 if (posdiff(selpoint, selstart) <
2510 posdiff(selend, selstart) / 2) {
2514 selanchor = selstart;
2516 selstate = DRAGGING;
2518 if (selstate != ABOUT_TO && selstate != DRAGGING)
2519 selanchor = selpoint;
2520 selstate = DRAGGING;
2521 if (poslt(selpoint, selanchor)) {
2522 selstart = selpoint;
2526 selstart = selanchor;
2531 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
2532 if (selstate == DRAGGING) {
2534 * We've completed a selection. We now transfer the
2535 * data to the clipboard.
2537 clipme(selstart, selend, selspace);
2538 selstate = SELECTED;
2540 selstate = NO_SELECTION;
2541 } else if (b == MBT_PASTE
2542 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
2546 get_clip((void **) &data, &len);
2551 sfree(paste_buffer);
2552 paste_pos = paste_hold = paste_len = 0;
2553 paste_buffer = smalloc(len);
2556 while (p < data + len) {
2557 while (p < data + len &&
2558 !(p <= data + len - sizeof(sel_nl) &&
2559 !memcmp(p, sel_nl, sizeof(sel_nl))))
2565 for (i = 0; i < p - q; i++) {
2566 c = xlat_kbd2tty(q[i]);
2567 paste_buffer[paste_len++] = c;
2571 if (p <= data + len - sizeof(sel_nl) &&
2572 !memcmp(p, sel_nl, sizeof(sel_nl))) {
2573 paste_buffer[paste_len++] = '\r';
2574 p += sizeof(sel_nl);
2579 /* Assume a small paste will be OK in one go. */
2580 if (paste_len < 256) {
2581 ldisc_send(paste_buffer, paste_len);
2583 sfree(paste_buffer);
2585 paste_pos = paste_hold = paste_len = 0;
2588 get_clip(NULL, NULL);
2598 sfree(paste_buffer);
2605 static long last_paste = 0;
2606 long now, paste_diff;
2611 /* Don't wait forever to paste */
2613 now = GetTickCount();
2614 paste_diff = now - last_paste;
2615 if (paste_diff >= 0 && paste_diff < 450)
2620 while (paste_pos < paste_len) {
2622 while (n + paste_pos < paste_len) {
2623 if (paste_buffer[paste_pos + n++] == '\r')
2626 ldisc_send(paste_buffer + paste_pos, n);
2629 if (paste_pos < paste_len) {
2634 sfree(paste_buffer);
2639 static void deselect(void)
2641 selstate = NO_SELECTION;
2642 selstart.x = selstart.y = selend.x = selend.y = 0;
2645 void term_deselect(void)
2651 int term_ldisc(int option)
2653 if (option == LD_ECHO)
2654 return term_echoing;
2655 if (option == LD_EDIT)
2656 return term_editing;
2661 * from_backend(), to get data from the backend for the terminal.
2663 void from_backend(int is_stderr, char *data, int len)
2666 if (inbuf_head >= INBUF_SIZE)
2668 inbuf[inbuf_head++] = *data++;
2673 * Log session traffic.
2675 void logtraffic(unsigned char c, int logmode)
2677 if (cfg.logtype > 0) {
2678 if (cfg.logtype == logmode) {
2679 /* deferred open file from pgm start? */
2688 /* open log file append/overwrite mode */
2698 sprintf(writemod, "wb"); /* default to rewrite */
2699 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
2703 i = askappend(cfg.logfilename);
2705 writemod[0] = 'a'; /* set append mode */
2706 else if (i == 0) { /* cancelled */
2708 cfg.logtype = 0; /* disable logging */
2713 lgfp = fopen(cfg.logfilename, writemod);
2714 if (lgfp) { /* enter into event log */
2715 sprintf(buf, "%s session log (%s mode) to file : ",
2716 (writemod[0] == 'a') ? "Appending" : "Writing new",
2717 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2718 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
2719 /* Make sure we do not exceed the output buffer size */
2720 strncat(buf, cfg.logfilename, 128);
2721 buf[strlen(buf)] = '\0';
2724 /* --- write header line iinto log file */
2725 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2728 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2730 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2734 void logfclose(void)