Fix flaky scrollbar update when scrollback was reset to bottom.
[u/mdw/putty] / terminal.c
CommitLineData
374330e2 1#include <windows.h>
2
3#include <stdio.h>
4#include <stdlib.h>
49bad831 5#include <ctype.h>
374330e2 6
e1c8e0ed 7#include <time.h>
c83de303 8#include <assert.h>
374330e2 9#include "putty.h"
4facdf84 10#include "tree234.h"
374330e2 11
4eeb7d09 12#define VT52_PLUS
13
32874aea 14#define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15#define CL_VT100 0x0002 /* VT100 */
16#define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17#define CL_VT102 0x0008 /* VT102 */
18#define CL_VT220 0x0010 /* VT220 */
19#define CL_VT320 0x0020 /* VT320 */
20#define CL_VT420 0x0040 /* VT420 */
21#define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22#define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23#define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24#define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25#define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
e14a5a13 26
c9def1b8 27#define TM_VT100 (CL_ANSIMIN|CL_VT100)
28#define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29#define TM_VT102 (TM_VT100AVO|CL_VT102)
30#define TM_VT220 (TM_VT102|CL_VT220)
31#define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32#define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
33
34#define TM_PUTTY (0xFFFF)
e14a5a13 35
36#define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
38 termstate=TOPLEVEL; \
39 break; \
40 }
c9def1b8 41#define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
43 termstate=TOPLEVEL; \
44 break; \
45 }
e14a5a13 46
47#define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
48
49static int compatibility_level = TM_PUTTY;
50
4facdf84 51static tree234 *scrollback; /* lines scrolled off top of screen */
52static tree234 *screen; /* lines on primary screen */
53static tree234 *alt_screen; /* lines on alternate screen */
c83de303 54static int disptop; /* distance scrolled back (0 or -ve) */
e14a5a13 55
374330e2 56static unsigned long *cpos; /* cursor position (convenience) */
4facdf84 57
374330e2 58static unsigned long *disptext; /* buffer of text on real screen */
4eeb7d09 59static unsigned long *dispcurs; /* location of cursor on real screen */
60static unsigned long curstype; /* type of cursor on real screen */
374330e2 61
c83de303 62#define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
156686ef 63
64struct beeptime {
65 struct beeptime *next;
66 long ticks;
67};
68static struct beeptime *beephead, *beeptail;
69int nbeeps;
70int beep_overloaded;
71long lastbeep;
72
4facdf84 73#define TSIZE (sizeof(unsigned long))
c83de303 74#define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
374330e2 75
76static unsigned long curr_attr, save_attr;
e14a5a13 77static unsigned long erase_char = ERASE_CHAR;
374330e2 78
4facdf84 79typedef struct {
80 int y, x;
81} pos;
82#define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83#define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84#define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
435aa044 85#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
4facdf84 86#define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87#define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
88
89static pos curs; /* cursor */
90static pos savecurs; /* saved cursor position */
374330e2 91static int marg_t, marg_b; /* scroll margins */
92static int dec_om; /* DEC origin mode flag */
93static int wrap, wrapnext; /* wrap flags */
94static int insert; /* insert-mode flag */
95static int cset; /* 0 or 1: which char set */
96static int save_cset, save_csattr; /* saved with cursor position */
8f22582c 97static int save_utf; /* saved with cursor position */
374330e2 98static int rvideo; /* global reverse video flag */
c83de303 99static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
e14a5a13 100static int cursor_on; /* cursor enabled flag */
101static int reset_132; /* Flag ESC c resets to 80 cols */
102static int use_bce; /* Use Background coloured erase */
103static int blinker; /* When blinking is the cursor on ? */
c9def1b8 104static int tblinker; /* When the blinking text is on */
105static int blink_is_real; /* Actually blink blinking text */
32874aea 106static int term_echoing; /* Does terminal want local echo? */
107static int term_editing; /* Does terminal want local edit? */
d3cb5465 108static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
4eeb7d09 109static int vt52_bold; /* Force bold on non-bold colours */
110static int utf_state; /* Is there a pending UTF-8 character */
111static int utf_char; /* and what is it so far. */
112static int utf_size; /* The size of the UTF character. */
374330e2 113
32874aea 114static int xterm_mouse; /* send mouse messages to app */
01c034ad 115
374330e2 116static unsigned long cset_attr[2];
117
118/*
119 * Saved settings on the alternate screen.
120 */
8f22582c 121static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
374330e2 122static int alt_t, alt_b;
123static int alt_which;
124
125#define ARGS_MAX 32 /* max # of esc sequence arguments */
064916ea 126#define ARG_DEFAULT 0 /* if an arg isn't specified */
374330e2 127#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
128static int esc_args[ARGS_MAX];
129static int esc_nargs;
130static int esc_query;
8b811b12 131#define ANSI(x,y) ((x)+((y)<<8))
132#define ANSI_QUE(x) ANSI(x,TRUE)
374330e2 133
134#define OSC_STR_MAX 2048
135static int osc_strlen;
32874aea 136static char osc_string[OSC_STR_MAX + 1];
374330e2 137static int osc_w;
138
c9def1b8 139static char id_string[1024] = "\033[?6c";
140
374330e2 141static unsigned char *tabs;
142
374330e2 143static enum {
cabfd08c 144 TOPLEVEL,
145 SEEN_ESC,
146 SEEN_CSI,
147 SEEN_OSC,
148 SEEN_OSC_W,
149
150 DO_CTRLS,
151
cabfd08c 152 SEEN_OSC_P,
153 OSC_STRING, OSC_MAYBE_ST,
e14a5a13 154 VT52_ESC,
155 VT52_Y1,
4eeb7d09 156 VT52_Y2,
157 VT52_FG,
158 VT52_BG
374330e2 159} termstate;
160
161static enum {
162 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
163} selstate;
164static enum {
165 SM_CHAR, SM_WORD, SM_LINE
166} selmode;
4facdf84 167static pos selstart, selend, selanchor;
374330e2 168
169static short wordness[256] = {
32874aea 170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
172 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
176 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
177 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
184 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
185 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
374330e2 186};
187
4eeb7d09 188#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
189static wchar_t sel_nl[] = SEL_NL;
190static wchar_t *paste_buffer = 0;
c9def1b8 191static int paste_len, paste_pos, paste_hold;
374330e2 192
193/*
194 * Internal prototypes.
195 */
32874aea 196static void do_paint(Context, int);
197static void erase_lots(int, int, int);
198static void swap_screen(int);
199static void update_sbar(void);
200static void deselect(void);
e1c8e0ed 201/* log session to file stuff ... */
202static FILE *lgfp = NULL;
203static void logtraffic(unsigned char c, int logmode);
66ee282a 204static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
374330e2 205
206/*
2371caac 207 * Resize a line to make it `cols' columns wide.
208 */
209unsigned long *resizeline(unsigned long *line, int cols)
210{
211 int i, oldlen;
212 unsigned long lineattrs;
213
214 if (line[0] != (unsigned long)cols) {
215 /*
216 * This line is the wrong length, which probably means it
217 * hasn't been accessed since a resize. Resize it now.
218 */
219 oldlen = line[0];
220 lineattrs = line[oldlen + 1];
221 line = srealloc(line, TSIZE * (2 + cols));
222 line[0] = cols;
223 for (i = oldlen; i < cols; i++)
224 line[i + 1] = ERASE_CHAR;
225 line[cols + 1] = lineattrs & LATTR_MODE;
226 }
227
228 return line;
229}
230
231/*
c83de303 232 * Retrieve a line of the screen or of the scrollback, according to
233 * whether the y coordinate is non-negative or negative
234 * (respectively).
235 */
32874aea 236unsigned long *lineptr(int y, int lineno)
237{
2371caac 238 unsigned long *line, *newline;
c83de303 239 tree234 *whichtree;
2371caac 240 int treeindex;
c83de303 241
242 if (y >= 0) {
243 whichtree = screen;
244 treeindex = y;
245 } else {
246 whichtree = scrollback;
247 treeindex = y + count234(scrollback);
248 }
249 line = index234(whichtree, treeindex);
250
251 /* We assume that we don't screw up and retrieve something out of range. */
c83de303 252 assert(line != NULL);
c83de303 253
2371caac 254 newline = resizeline(line, cols);
255 if (newline != line) {
c83de303 256 delpos234(whichtree, treeindex);
2371caac 257 addpos234(whichtree, newline, treeindex);
04dcfaa2 258 line = newline;
c83de303 259 }
260
32874aea 261 return line + 1;
c83de303 262}
32874aea 263
c83de303 264#define lineptr(x) lineptr(x,__LINE__)
265/*
374330e2 266 * Set up power-on settings for the terminal.
267 */
32874aea 268static void power_on(void)
269{
4facdf84 270 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
374330e2 271 alt_t = marg_t = 0;
272 if (rows != -1)
273 alt_b = marg_b = rows - 1;
274 else
275 alt_b = marg_b = 0;
276 if (cols != -1) {
277 int i;
278 for (i = 0; i < cols; i++)
279 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
280 }
281 alt_om = dec_om = cfg.dec_om;
282 alt_wnext = wrapnext = alt_ins = insert = FALSE;
283 alt_wrap = wrap = cfg.wrap_mode;
284 alt_cset = cset = 0;
8f22582c 285 alt_utf = utf = 0;
d3cb5465 286 alt_sco_acs = sco_acs = 0;
374330e2 287 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
288 rvideo = 0;
156686ef 289 in_vbell = FALSE;
e14a5a13 290 cursor_on = 1;
4eeb7d09 291 big_cursor = 0;
374330e2 292 save_attr = curr_attr = ATTR_DEFAULT;
0965bee0 293 term_editing = term_echoing = FALSE;
32874aea 294 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
374330e2 295 app_cursor_keys = cfg.app_cursor;
296 app_keypad_keys = cfg.app_keypad;
c9def1b8 297 use_bce = cfg.bce;
298 blink_is_real = cfg.blinktext;
e14a5a13 299 erase_char = ERASE_CHAR;
374330e2 300 alt_which = 0;
301 {
302 int i;
303 for (i = 0; i < 256; i++)
304 wordness[i] = cfg.wordness[i];
305 }
4facdf84 306 if (screen) {
32874aea 307 swap_screen(1);
308 erase_lots(FALSE, TRUE, TRUE);
309 swap_screen(0);
310 erase_lots(FALSE, TRUE, TRUE);
374330e2 311 }
312}
313
314/*
315 * Force a screen update.
316 */
32874aea 317void term_update(void)
318{
374330e2 319 Context ctx;
320 ctx = get_ctx();
321 if (ctx) {
4f892125 322 int need_sbar_update = seen_disp_event;
32874aea 323 if ((seen_key_event && (cfg.scroll_on_key)) ||
324 (seen_disp_event && (cfg.scroll_on_disp))) {
4facdf84 325 disptop = 0; /* return to main screen */
cabfd08c 326 seen_disp_event = seen_key_event = 0;
4f892125 327 need_sbar_update = TRUE;
cabfd08c 328 }
4f892125 329 if (need_sbar_update)
330 update_sbar();
32874aea 331 do_paint(ctx, TRUE);
332 sys_cursor(curs.x, curs.y - disptop);
333 free_ctx(ctx);
374330e2 334 }
335}
336
337/*
338 * Same as power_on(), but an external function.
339 */
32874aea 340void term_pwron(void)
341{
374330e2 342 power_on();
343 fix_cpos;
4facdf84 344 disptop = 0;
374330e2 345 deselect();
346 term_update();
347}
348
349/*
350 * Clear the scrollback.
351 */
32874aea 352void term_clrsb(void)
353{
4facdf84 354 unsigned long *line;
355 disptop = 0;
356 while ((line = delpos234(scrollback, 0)) != NULL) {
357 sfree(line);
358 }
374330e2 359 update_sbar();
360}
361
362/*
363 * Initialise the terminal.
364 */
32874aea 365void term_init(void)
366{
4facdf84 367 screen = alt_screen = scrollback = NULL;
368 disptop = 0;
4eeb7d09 369 disptext = dispcurs = NULL;
374330e2 370 tabs = NULL;
374330e2 371 deselect();
372 rows = cols = -1;
374330e2 373 power_on();
156686ef 374 beephead = beeptail = NULL;
375 nbeeps = 0;
376 lastbeep = FALSE;
377 beep_overloaded = FALSE;
374330e2 378}
379
380/*
381 * Set up the terminal for a given size.
382 */
32874aea 383void term_size(int newrows, int newcols, int newsavelines)
384{
2d466ffd 385 tree234 *newalt;
386 unsigned long *newdisp, *line;
387 int i, j;
c83de303 388 int sblen;
d6832394 389 int save_alt_which = alt_which;
390
374330e2 391 if (newrows == rows && newcols == cols && newsavelines == savelines)
392 return; /* nothing to do */
393
d6832394 394 deselect();
395 swap_screen(0);
396
374330e2 397 alt_t = marg_t = 0;
398 alt_b = marg_b = newrows - 1;
399
c83de303 400 if (rows == -1) {
401 scrollback = newtree234(NULL);
402 screen = newtree234(NULL);
403 rows = 0;
404 }
405
406 /*
407 * Resize the screen and scrollback. We only need to shift
408 * lines around within our data structures, because lineptr()
409 * will take care of resizing each individual line if
410 * necessary. So:
411 *
412 * - If the new screen and the old screen differ in length, we
413 * must shunt some lines in from the scrollback or out to
414 * the scrollback.
415 *
416 * - If doing that fails to provide us with enough material to
417 * fill the new screen (i.e. the number of rows needed in
418 * the new screen exceeds the total number in the previous
419 * screen+scrollback), we must invent some blank lines to
420 * cover the gap.
421 *
422 * - Then, if the new scrollback length is less than the
423 * amount of scrollback we actually have, we must throw some
424 * away.
425 */
426 sblen = count234(scrollback);
cdd6c586 427 /* Do this loop to expand the screen if newrows > rows */
428 for (i = rows; i < newrows; i++) {
32874aea 429 if (sblen > 0) {
430 line = delpos234(scrollback, --sblen);
431 } else {
432 line = smalloc(TSIZE * (newcols + 2));
433 line[0] = newcols;
434 for (j = 0; j <= newcols; j++)
435 line[j + 1] = ERASE_CHAR;
436 }
437 addpos234(screen, line, 0);
cdd6c586 438 }
439 /* Do this loop to shrink the screen if newrows < rows */
440 for (i = newrows; i < rows; i++) {
32874aea 441 line = delpos234(screen, 0);
442 addpos234(scrollback, line, sblen++);
c83de303 443 }
444 assert(count234(screen) == newrows);
445 while (sblen > newsavelines) {
446 line = delpos234(scrollback, 0);
447 sfree(line);
448 sblen--;
c9def1b8 449 }
c83de303 450 assert(count234(scrollback) <= newsavelines);
4facdf84 451 disptop = 0;
374330e2 452
32874aea 453 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
454 for (i = 0; i < newrows * (newcols + 1); i++)
374330e2 455 newdisp[i] = ATTR_INVALID;
32874aea 456 sfree(disptext);
374330e2 457 disptext = newdisp;
4eeb7d09 458 dispcurs = NULL;
374330e2 459
4facdf84 460 newalt = newtree234(NULL);
32874aea 461 for (i = 0; i < newrows; i++) {
462 line = smalloc(TSIZE * (newcols + 2));
c83de303 463 line[0] = newcols;
4facdf84 464 for (j = 0; j <= newcols; j++)
32874aea 465 line[j + 1] = erase_char;
4facdf84 466 addpos234(newalt, line, i);
467 }
468 if (alt_screen) {
469 while (NULL != (line = delpos234(alt_screen, 0)))
470 sfree(line);
471 freetree234(alt_screen);
472 }
473 alt_screen = newalt;
374330e2 474
32874aea 475 tabs = srealloc(tabs, newcols * sizeof(*tabs));
374330e2 476 {
477 int i;
478 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
479 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
480 }
481
482 if (rows > 0)
4facdf84 483 curs.y += newrows - rows;
484 if (curs.y < 0)
485 curs.y = 0;
486 if (curs.y >= newrows)
32874aea 487 curs.y = newrows - 1;
4facdf84 488 if (curs.x >= newcols)
32874aea 489 curs.x = newcols - 1;
374330e2 490 alt_x = alt_y = 0;
491 wrapnext = alt_wnext = FALSE;
492
493 rows = newrows;
494 cols = newcols;
495 savelines = newsavelines;
496 fix_cpos;
497
d6832394 498 swap_screen(save_alt_which);
499
374330e2 500 update_sbar();
501 term_update();
5a73255e 502 back->size();
374330e2 503}
504
505/*
506 * Swap screens.
507 */
32874aea 508static void swap_screen(int which)
509{
374330e2 510 int t;
4facdf84 511 tree234 *ttr;
374330e2 512
513 if (which == alt_which)
514 return;
515
516 alt_which = which;
517
32874aea 518 ttr = alt_screen;
519 alt_screen = screen;
520 screen = ttr;
521 t = curs.x;
522 curs.x = alt_x;
523 alt_x = t;
524 t = curs.y;
525 curs.y = alt_y;
526 alt_y = t;
527 t = marg_t;
528 marg_t = alt_t;
529 alt_t = t;
530 t = marg_b;
531 marg_b = alt_b;
532 alt_b = t;
533 t = dec_om;
534 dec_om = alt_om;
535 alt_om = t;
536 t = wrap;
537 wrap = alt_wrap;
538 alt_wrap = t;
539 t = wrapnext;
540 wrapnext = alt_wnext;
541 alt_wnext = t;
542 t = insert;
543 insert = alt_ins;
544 alt_ins = t;
545 t = cset;
546 cset = alt_cset;
547 alt_cset = t;
8f22582c 548 t = utf;
549 utf = alt_utf;
550 alt_utf = t;
d3cb5465 551 t = sco_acs;
552 sco_acs = alt_sco_acs;
553 alt_sco_acs = t;
374330e2 554
555 fix_cpos;
556}
557
558/*
374330e2 559 * Update the scroll bar.
560 */
32874aea 561static void update_sbar(void)
562{
260f3dec 563 int nscroll;
374330e2 564
4facdf84 565 nscroll = count234(scrollback);
566
32874aea 567 set_sbar(nscroll + rows, nscroll + disptop, rows);
374330e2 568}
569
570/*
571 * Check whether the region bounded by the two pointers intersects
572 * the scroll region, and de-select the on-screen selection if so.
573 */
32874aea 574static void check_selection(pos from, pos to)
575{
4facdf84 576 if (poslt(from, selend) && poslt(selstart, to))
374330e2 577 deselect();
578}
579
580/*
581 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
582 * for backward.) `sb' is TRUE if the scrolling is permitted to
583 * affect the scrollback buffer.
a4450583 584 *
585 * NB this function invalidates all pointers into lines of the
586 * screen data structures. In particular, you MUST call fix_cpos
587 * after calling scroll() and before doing anything else that
588 * uses the cpos shortcut pointer.
374330e2 589 */
32874aea 590static void scroll(int topline, int botline, int lines, int sb)
591{
4facdf84 592 unsigned long *line, *line2;
a792432e 593 int i, seltop;
c9def1b8 594
7af9fa7f 595 if (topline != 0 || alt_which != 0)
4facdf84 596 sb = FALSE;
597
598 if (lines < 0) {
599 while (lines < 0) {
600 line = delpos234(screen, botline);
2371caac 601 line = resizeline(line, cols);
4facdf84 602 for (i = 0; i < cols; i++)
32874aea 603 line[i + 1] = erase_char;
604 line[cols + 1] = 0;
4facdf84 605 addpos234(screen, line, topline);
606
607 if (selstart.y >= topline && selstart.y <= botline) {
608 selstart.y++;
609 if (selstart.y > botline) {
610 selstart.y = botline;
611 selstart.x = 0;
c9def1b8 612 }
c9def1b8 613 }
4facdf84 614 if (selend.y >= topline && selend.y <= botline) {
615 selend.y++;
616 if (selend.y > botline) {
617 selend.y = botline;
618 selend.x = 0;
c9def1b8 619 }
c9def1b8 620 }
c9def1b8 621
4facdf84 622 lines++;
c9def1b8 623 }
374330e2 624 } else {
4facdf84 625 while (lines > 0) {
626 line = delpos234(screen, topline);
c83de303 627 if (sb && savelines > 0) {
4facdf84 628 int sblen = count234(scrollback);
629 /*
630 * We must add this line to the scrollback. We'll
631 * remove a line from the top of the scrollback to
632 * replace it, or allocate a new one if the
633 * scrollback isn't full.
634 */
c83de303 635 if (sblen == savelines) {
4facdf84 636 sblen--, line2 = delpos234(scrollback, 0);
c83de303 637 } else {
32874aea 638 line2 = smalloc(TSIZE * (cols + 2));
c83de303 639 line2[0] = cols;
640 }
4facdf84 641 addpos234(scrollback, line, sblen);
642 line = line2;
a792432e 643
644 /*
645 * If the user is currently looking at part of the
646 * scrollback, and they haven't enabled any options
647 * that are going to reset the scrollback as a
648 * result of this movement, then the chances are
649 * they'd like to keep looking at the same line. So
650 * we move their viewpoint at the same rate as the
651 * scroll, at least until their viewpoint hits the
652 * top end of the scrollback buffer, at which point
653 * we don't have the choice any more.
654 *
655 * Thanks to Jan Holmen Holsten for the idea and
656 * initial implementation.
657 */
658 if (disptop > -savelines && disptop < 0)
659 disptop--;
4facdf84 660 }
2371caac 661 line = resizeline(line, cols);
4facdf84 662 for (i = 0; i < cols; i++)
32874aea 663 line[i + 1] = erase_char;
664 line[cols + 1] = 0;
4facdf84 665 addpos234(screen, line, botline);
666
a792432e 667 /*
668 * If the selection endpoints move into the scrollback,
669 * we keep them moving until they hit the top. However,
670 * of course, if the line _hasn't_ moved into the
671 * scrollback then we don't do this, and cut them off
672 * at the top of the scroll region.
d48db8b4 673 *
674 * This applies to selstart and selend (for an existing
675 * selection), and also selanchor (for one being
676 * selected as we speak).
a792432e 677 */
678 seltop = sb ? -savelines : 0;
679
680 if (selstart.y >= seltop && selstart.y <= botline) {
4facdf84 681 selstart.y--;
a792432e 682 if (selstart.y < seltop) {
683 selstart.y = seltop;
4facdf84 684 selstart.x = 0;
685 }
686 }
a792432e 687 if (selend.y >= seltop && selend.y <= botline) {
4facdf84 688 selend.y--;
a792432e 689 if (selend.y < seltop) {
690 selend.y = seltop;
4facdf84 691 selend.x = 0;
692 }
693 }
d48db8b4 694 if (selanchor.y >= seltop && selanchor.y <= botline) {
695 selanchor.y--;
696 if (selanchor.y < seltop) {
697 selanchor.y = seltop;
698 selanchor.x = 0;
699 }
700 }
4facdf84 701
702 lines--;
c9def1b8 703 }
374330e2 704 }
374330e2 705}
706
707/*
708 * Move the cursor to a given position, clipping at boundaries. We
709 * may or may not want to clip at the scroll margin: marg_clip is 0
710 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
711 * even _being_ outside the margins.
712 */
32874aea 713static void move(int x, int y, int marg_clip)
714{
374330e2 715 if (x < 0)
716 x = 0;
717 if (x >= cols)
32874aea 718 x = cols - 1;
374330e2 719 if (marg_clip) {
4facdf84 720 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
374330e2 721 y = marg_t;
4facdf84 722 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
374330e2 723 y = marg_b;
724 }
725 if (y < 0)
726 y = 0;
727 if (y >= rows)
32874aea 728 y = rows - 1;
4facdf84 729 curs.x = x;
730 curs.y = y;
374330e2 731 fix_cpos;
732 wrapnext = FALSE;
733}
734
735/*
736 * Save or restore the cursor and SGR mode.
737 */
32874aea 738static void save_cursor(int save)
739{
374330e2 740 if (save) {
4facdf84 741 savecurs = curs;
374330e2 742 save_attr = curr_attr;
743 save_cset = cset;
8f22582c 744 save_utf = utf;
374330e2 745 save_csattr = cset_attr[cset];
d3cb5465 746 save_sco_acs = sco_acs;
374330e2 747 } else {
4facdf84 748 curs = savecurs;
c9def1b8 749 /* Make sure the window hasn't shrunk since the save */
32874aea 750 if (curs.x >= cols)
751 curs.x = cols - 1;
752 if (curs.y >= rows)
753 curs.y = rows - 1;
c9def1b8 754
374330e2 755 curr_attr = save_attr;
756 cset = save_cset;
8f22582c 757 utf = save_utf;
374330e2 758 cset_attr[cset] = save_csattr;
d3cb5465 759 sco_acs = save_sco_acs;
374330e2 760 fix_cpos;
32874aea 761 if (use_bce)
8f22582c 762 erase_char = (' ' | ATTR_ASCII |
763 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
374330e2 764 }
765}
766
767/*
768 * Erase a large portion of the screen: the whole screen, or the
769 * whole line, or parts thereof.
770 */
32874aea 771static void erase_lots(int line_only, int from_begin, int to_end)
772{
260f3dec 773 pos start, end;
4facdf84 774 int erase_lattr;
775 unsigned long *ldata;
374330e2 776
777 if (line_only) {
4facdf84 778 start.y = curs.y;
779 start.x = 0;
780 end.y = curs.y + 1;
781 end.x = 0;
782 erase_lattr = FALSE;
374330e2 783 } else {
4facdf84 784 start.y = 0;
785 start.x = 0;
786 end.y = rows;
787 end.x = 0;
788 erase_lattr = TRUE;
789 }
790 if (!from_begin) {
791 start = curs;
374330e2 792 }
4facdf84 793 if (!to_end) {
794 end = curs;
2a6dfe0e 795 incpos(end);
4facdf84 796 }
32874aea 797 check_selection(start, end);
e14a5a13 798
799 /* Clear screen also forces a full window redraw, just in case. */
4facdf84 800 if (start.y == 0 && start.x == 0 && end.y == rows)
32874aea 801 term_invalidate();
e14a5a13 802
4facdf84 803 ldata = lineptr(start.y);
804 while (poslt(start, end)) {
cfbeddf3 805 if (start.x == cols && !erase_lattr)
4eeb7d09 806 ldata[start.x] &= ~LATTR_WRAPPED;
4facdf84 807 else
808 ldata[start.x] = erase_char;
c83de303 809 if (incpos(start) && start.y < rows)
4facdf84 810 ldata = lineptr(start.y);
811 }
374330e2 812}
813
814/*
815 * Insert or delete characters within the current line. n is +ve if
816 * insertion is desired, and -ve for deletion.
817 */
32874aea 818static void insch(int n)
819{
374330e2 820 int dir = (n < 0 ? -1 : +1);
821 int m;
4facdf84 822 pos cursplus;
823 unsigned long *ldata;
374330e2 824
825 n = (n < 0 ? -n : n);
4facdf84 826 if (n > cols - curs.x)
827 n = cols - curs.x;
828 m = cols - curs.x - n;
829 cursplus.y = curs.y;
830 cursplus.x = curs.x + n;
32874aea 831 check_selection(curs, cursplus);
4facdf84 832 ldata = lineptr(curs.y);
374330e2 833 if (dir < 0) {
32874aea 834 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
374330e2 835 while (n--)
4facdf84 836 ldata[curs.x + m++] = erase_char;
374330e2 837 } else {
32874aea 838 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
374330e2 839 while (n--)
4facdf84 840 ldata[curs.x + n] = erase_char;
374330e2 841 }
842}
843
844/*
845 * Toggle terminal mode `mode' to state `state'. (`query' indicates
846 * whether the mode is a DEC private one or a normal one.)
847 */
32874aea 848static void toggle_mode(int mode, int query, int state)
849{
7594731b 850 long ticks;
01c034ad 851
32874aea 852 if (query)
853 switch (mode) {
854 case 1: /* application cursor keys */
855 app_cursor_keys = state;
856 break;
857 case 2: /* VT52 mode */
858 vt52_mode = !state;
4eeb7d09 859 if (vt52_mode) {
860 blink_is_real = FALSE;
861 vt52_bold = FALSE;
862 } else {
863 blink_is_real = cfg.blinktext;
864 }
32874aea 865 break;
866 case 3: /* 80/132 columns */
867 deselect();
5a73255e 868 request_resize(state ? 132 : 80, rows);
32874aea 869 reset_132 = state;
870 break;
871 case 5: /* reverse video */
872 /*
873 * Toggle reverse video. If we receive an OFF within the
874 * visual bell timeout period after an ON, we trigger an
875 * effective visual bell, so that ESC[?5hESC[?5l will
876 * always be an actually _visible_ visual bell.
877 */
878 ticks = GetTickCount();
879 if (rvideo && !state && /* we're turning it off */
880 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
881 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
882 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
883 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
884 } else if (!rvideo && state) {
885 /* This is an ON, so we notice the time and save it. */
886 rvbell_timeout = ticks + VBELL_TIMEOUT;
887 }
888 rvideo = state;
889 seen_disp_event = TRUE;
890 if (state)
891 term_update();
892 break;
893 case 6: /* DEC origin mode */
894 dec_om = state;
895 break;
896 case 7: /* auto wrap */
897 wrap = state;
898 break;
899 case 8: /* auto key repeat */
900 repeat_off = !state;
901 break;
902 case 10: /* set local edit mode */
903 term_editing = state;
904 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
905 break;
906 case 25: /* enable/disable cursor */
907 compatibility2(OTHER, VT220);
908 cursor_on = state;
909 seen_disp_event = TRUE;
910 break;
911 case 47: /* alternate screen */
912 compatibility(OTHER);
913 deselect();
914 swap_screen(state);
915 disptop = 0;
916 break;
917 case 1000: /* xterm mouse 1 */
918 xterm_mouse = state ? 1 : 0;
919 set_raw_mouse_mode(state);
920 break;
921 case 1002: /* xterm mouse 2 */
922 xterm_mouse = state ? 2 : 0;
923 set_raw_mouse_mode(state);
924 break;
925 } else
926 switch (mode) {
927 case 4: /* set insert mode */
928 compatibility(VT102);
929 insert = state;
930 break;
931 case 12: /* set echo mode */
932 term_echoing = !state;
933 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
934 break;
935 case 20: /* Return sends ... */
936 cr_lf_return = state;
937 break;
4eeb7d09 938 case 34: /* Make cursor BIG */
939 compatibility2(OTHER, VT220);
940 big_cursor = !state;
7594731b 941 }
374330e2 942}
943
944/*
945 * Process an OSC sequence: set window title or icon name.
946 */
32874aea 947static void do_osc(void)
948{
374330e2 949 if (osc_w) {
950 while (osc_strlen--)
32874aea 951 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
374330e2 952 } else {
953 osc_string[osc_strlen] = '\0';
954 switch (esc_args[0]) {
955 case 0:
956 case 1:
32874aea 957 set_icon(osc_string);
374330e2 958 if (esc_args[0] == 1)
959 break;
960 /* fall through: parameter 0 means set both */
961 case 2:
962 case 21:
32874aea 963 set_title(osc_string);
374330e2 964 break;
965 }
966 }
967}
968
969/*
970 * Remove everything currently in `inbuf' and stick it up on the
971 * in-memory display. There's a big state machine in here to
972 * process escape sequences...
973 */
32874aea 974void term_out(void)
975{
c9def1b8 976 int c, inbuf_reap;
977
5a16642f 978 /*
979 * Optionally log the session traffic to a file. Useful for
980 * debugging and possibly also useful for actual logging.
981 */
982 if (cfg.logtype == LGTYP_DEBUG)
983 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
984 logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
985 }
986
32874aea 987 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
988 c = inbuf[inbuf_reap];
374330e2 989
e14a5a13 990 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
991 * be able to display 8-bit characters, but I'll let that go 'cause
992 * of i18n.
993 */
4eeb7d09 994
995 /* First see about all those translations. */
996 if (termstate == TOPLEVEL) {
8f22582c 997 if (in_utf)
4eeb7d09 998 switch (utf_state) {
999 case 0:
1000 if (c < 0x80) {
8f22582c 1001 /* UTF-8 must be stateless so we ignore iso2022. */
1002 if (unitab_ctrl[c] != 0xFF)
1003 c = unitab_ctrl[c];
1004 else c = ((unsigned char)c) | ATTR_ASCII;
1005 break;
4eeb7d09 1006 } else if ((c & 0xe0) == 0xc0) {
1007 utf_size = utf_state = 1;
1008 utf_char = (c & 0x1f);
1009 } else if ((c & 0xf0) == 0xe0) {
1010 utf_size = utf_state = 2;
1011 utf_char = (c & 0x0f);
1012 } else if ((c & 0xf8) == 0xf0) {
1013 utf_size = utf_state = 3;
1014 utf_char = (c & 0x07);
1015 } else if ((c & 0xfc) == 0xf8) {
1016 utf_size = utf_state = 4;
1017 utf_char = (c & 0x03);
1018 } else if ((c & 0xfe) == 0xfc) {
1019 utf_size = utf_state = 5;
1020 utf_char = (c & 0x01);
1021 } else {
1022 c = UCSERR;
1023 break;
1024 }
1025 continue;
1026 case 1:
1027 case 2:
1028 case 3:
1029 case 4:
1030 case 5:
1031 if ((c & 0xC0) != 0x80) {
5a16642f 1032 inbuf_reap--;
1033 c = UCSERR;
1034 utf_state = 0;
4eeb7d09 1035 break;
1036 }
1037 utf_char = (utf_char << 6) | (c & 0x3f);
1038 if (--utf_state)
1039 continue;
1040
1041 c = utf_char;
1042
1043 /* Is somebody trying to be evil! */
1044 if (c < 0x80 ||
1045 (c < 0x800 && utf_size >= 2) ||
1046 (c < 0x10000 && utf_size >= 3) ||
1047 (c < 0x200000 && utf_size >= 4) ||
1048 (c < 0x4000000 && utf_size >= 5))
1049 c = UCSERR;
1050
1051 /* Unicode line separator and paragraph separator are CR-LF */
1052 if (c == 0x2028 || c == 0x2029)
1053 c = 0x85;
1054
1055 /* High controls are probably a Baaad idea too. */
1056 if (c < 0xA0)
1057 c = 0xFFFD;
1058
1059 /* The UTF-16 surrogates are not nice either. */
1060 /* The standard give the option of decoding these:
1061 * I don't want to! */
1062 if (c >= 0xD800 && c < 0xE000)
1063 c = UCSERR;
1064
1065 /* ISO 10646 characters now limited to UTF-16 range. */
1066 if (c > 0x10FFFF)
1067 c = UCSERR;
1068
1069 /* This is currently a TagPhobic application.. */
1070 if (c >= 0xE0000 && c <= 0xE007F)
1071 continue;
1072
1073 /* U+FEFF is best seen as a null. */
1074 if (c == 0xFEFF)
1075 continue;
1076 /* But U+FFFE is an error. */
1077 if (c == 0xFFFE || c == 0xFFFF)
1078 c = UCSERR;
1079
1080 /* Oops this is a 16bit implementation */
1081 if (c >= 0x10000)
1082 c = 0xFFFD;
1083 break;
d3cb5465 1084 }
1085 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1086 else if(sco_acs &&
1087 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1088 {
1089 if (sco_acs == 2) c ^= 0x80;
1090 c |= ATTR_SCOACS;
4eeb7d09 1091 } else {
4eeb7d09 1092 switch (cset_attr[cset]) {
1093 /*
1094 * Linedraw characters are different from 'ESC ( B'
1095 * only for a small range. For ones outside that
1096 * range, make sure we use the same font as well as
1097 * the same encoding.
1098 */
1099 case ATTR_LINEDRW:
1100 if (unitab_ctrl[c] != 0xFF)
1101 c = unitab_ctrl[c];
1102 else
1103 c = ((unsigned char) c) | ATTR_LINEDRW;
1104 break;
1105
1106 case ATTR_GBCHR:
1107 /* If UK-ASCII, make the '#' a LineDraw Pound */
1108 if (c == '#') {
1109 c = '}' | ATTR_LINEDRW;
1110 break;
1111 }
1112 /*FALLTHROUGH*/ case ATTR_ASCII:
1113 if (unitab_ctrl[c] != 0xFF)
1114 c = unitab_ctrl[c];
1115 else
1116 c = ((unsigned char) c) | ATTR_ASCII;
1117 break;
d3cb5465 1118 case ATTR_SCOACS:
1119 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1120 break;
4eeb7d09 1121 }
1122 }
1123 }
1124
1125 /* How about C1 controls ? */
1126 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1127 has_compat(VT220)) {
1128 termstate = SEEN_ESC;
1129 esc_query = FALSE;
1130 c = '@' + (c & 0x1F);
1131 }
1132
1133 /* Or the GL control. */
1134 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1135 if (curs.x && !wrapnext)
1136 curs.x--;
1137 wrapnext = FALSE;
1138 fix_cpos;
1139 *cpos = (' ' | curr_attr | ATTR_ASCII);
1140 } else
1141 /* Or normal C0 controls. */
1142 if ((c & -32) == 0 && termstate < DO_CTRLS) {
374330e2 1143 switch (c) {
1144 case '\005': /* terminal type query */
e14a5a13 1145 /* Strictly speaking this is VT100 but a VT100 defaults to
1146 * no response. Other terminals respond at their option.
1147 *
1148 * Don't put a CR in the default string as this tends to
1149 * upset some weird software.
1150 *
1151 * An xterm returns "xterm" (5 characters)
1152 */
32874aea 1153 compatibility(ANSIMIN);
e7fbcdd8 1154 {
1155 char abuf[256], *s, *d;
32874aea 1156 int state = 0;
1157 for (s = cfg.answerback, d = abuf; *s; s++) {
1158 if (state) {
1159 if (*s >= 'a' && *s <= 'z')
1160 *d++ = (*s - ('a' - 1));
1161 else if ((*s >= '@' && *s <= '_') ||
1162 *s == '?' || (*s & 0x80))
1163 *d++ = ('@' ^ *s);
1164 else if (*s == '~')
1165 *d++ = '^';
1166 state = 0;
1167 } else if (*s == '^') {
1168 state = 1;
1169 } else
4eeb7d09 1170 *d++ = *s;
e7fbcdd8 1171 }
4eeb7d09 1172 lpage_send(CP_ACP, abuf, d - abuf);
e7fbcdd8 1173 }
374330e2 1174 break;
1175 case '\007':
156686ef 1176 {
1177 struct beeptime *newbeep;
1178 long ticks;
1179
1180 ticks = GetTickCount();
156686ef 1181
1182 if (!beep_overloaded) {
1183 newbeep = smalloc(sizeof(struct beeptime));
1184 newbeep->ticks = ticks;
1185 newbeep->next = NULL;
1186 if (!beephead)
1187 beephead = newbeep;
1188 else
1189 beeptail->next = newbeep;
1190 beeptail = newbeep;
1191 nbeeps++;
1192 }
1193
1194 /*
1195 * Throw out any beeps that happened more than
1196 * t seconds ago.
1197 */
1198 while (beephead &&
7f4968e6 1199 beephead->ticks < ticks - cfg.bellovl_t) {
156686ef 1200 struct beeptime *tmp = beephead;
1201 beephead = tmp->next;
156686ef 1202 sfree(tmp);
1203 if (!beephead)
1204 beeptail = NULL;
1205 nbeeps--;
1206 }
1207
1208 if (cfg.bellovl && beep_overloaded &&
32874aea 1209 ticks - lastbeep >= cfg.bellovl_s) {
156686ef 1210 /*
1211 * If we're currently overloaded and the
1212 * last beep was more than s seconds ago,
1213 * leave overload mode.
1214 */
156686ef 1215 beep_overloaded = FALSE;
1216 } else if (cfg.bellovl && !beep_overloaded &&
1217 nbeeps >= cfg.bellovl_n) {
1218 /*
1219 * Now, if we have n or more beeps
1220 * remaining in the queue, go into overload
1221 * mode.
1222 */
156686ef 1223 beep_overloaded = TRUE;
1224 }
1225 lastbeep = ticks;
1226
1227 /*
1228 * Perform an actual beep if we're not overloaded.
1229 */
f8a28d1f 1230 if (!cfg.bellovl || !beep_overloaded) {
1231 beep(cfg.beep);
1232 if (cfg.beep == BELL_VISUAL) {
156686ef 1233 in_vbell = TRUE;
1234 vbell_timeout = ticks + VBELL_TIMEOUT;
1235 term_update();
1236 }
1237 }
4facdf84 1238 disptop = 0;
156686ef 1239 }
374330e2 1240 break;
1241 case '\b':
967a161e 1242 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
4facdf84 1243 else if (curs.x == 0 && curs.y > 0)
32874aea 1244 curs.x = cols - 1, curs.y--;
374330e2 1245 else if (wrapnext)
1246 wrapnext = FALSE;
1247 else
4facdf84 1248 curs.x--;
374330e2 1249 fix_cpos;
cabfd08c 1250 seen_disp_event = TRUE;
374330e2 1251 break;
1252 case '\016':
32874aea 1253 compatibility(VT100);
374330e2 1254 cset = 1;
1255 break;
1256 case '\017':
32874aea 1257 compatibility(VT100);
374330e2 1258 cset = 0;
1259 break;
1260 case '\033':
32874aea 1261 if (vt52_mode)
1262 termstate = VT52_ESC;
e14a5a13 1263 else {
1264 compatibility(ANSIMIN);
1265 termstate = SEEN_ESC;
4eeb7d09 1266 esc_query = FALSE;
e14a5a13 1267 }
374330e2 1268 break;
374330e2 1269 case '\r':
4facdf84 1270 curs.x = 0;
374330e2 1271 wrapnext = FALSE;
1272 fix_cpos;
cabfd08c 1273 seen_disp_event = TRUE;
c9def1b8 1274 paste_hold = 0;
32874aea 1275 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1276 break;
374330e2 1277 case '\014':
1a837633 1278 if (has_compat(SCOANSI)) {
1279 move(0, 0, 0);
1280 erase_lots(FALSE, FALSE, TRUE);
1281 disptop = 0;
1282 wrapnext = FALSE;
1283 seen_disp_event = 1;
1284 break;
1285 }
1286 case '\013':
32874aea 1287 compatibility(VT100);
374330e2 1288 case '\n':
4facdf84 1289 if (curs.y == marg_b)
32874aea 1290 scroll(marg_t, marg_b, 1, TRUE);
1291 else if (curs.y < rows - 1)
4facdf84 1292 curs.y++;
cabfd08c 1293 if (cfg.lfhascr)
4facdf84 1294 curs.x = 0;
374330e2 1295 fix_cpos;
1296 wrapnext = FALSE;
cabfd08c 1297 seen_disp_event = 1;
c9def1b8 1298 paste_hold = 0;
32874aea 1299 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1300 break;
1301 case '\t':
374330e2 1302 {
4facdf84 1303 pos old_curs = curs;
1304 unsigned long *ldata = lineptr(curs.y);
c9def1b8 1305
1306 do {
4facdf84 1307 curs.x++;
32874aea 1308 } while (curs.x < cols - 1 && !tabs[curs.x]);
c9def1b8 1309
32874aea 1310 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1311 if (curs.x >= cols / 2)
1312 curs.x = cols / 2 - 1;
1313 } else {
4facdf84 1314 if (curs.x >= cols)
32874aea 1315 curs.x = cols - 1;
c9def1b8 1316 }
1317
374330e2 1318 fix_cpos;
32874aea 1319 check_selection(old_curs, curs);
374330e2 1320 }
cabfd08c 1321 seen_disp_event = TRUE;
374330e2 1322 break;
cabfd08c 1323 }
32874aea 1324 } else
1325 switch (termstate) {
1326 case TOPLEVEL:
1327 /* Only graphic characters get this far, ctrls are stripped above */
1328 if (wrapnext && wrap) {
4eeb7d09 1329 cpos[1] |= LATTR_WRAPPED;
32874aea 1330 if (curs.y == marg_b)
1331 scroll(marg_t, marg_b, 1, TRUE);
1332 else if (curs.y < rows - 1)
1333 curs.y++;
1334 curs.x = 0;
1335 fix_cpos;
1336 wrapnext = FALSE;
374330e2 1337 }
32874aea 1338 if (insert)
1339 insch(1);
1340 if (selstate != NO_SELECTION) {
1341 pos cursplus = curs;
1342 incpos(cursplus);
1343 check_selection(curs, cursplus);
374330e2 1344 }
4eeb7d09 1345 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1346 logtraffic((unsigned char) c, LGTYP_ASCII);
1347 {
1348 extern int wcwidth(wchar_t ucs);
1349 int width = 0;
1350 if (DIRECT_CHAR(c))
1351 width = 1;
1352 if (!width)
1353 width = wcwidth((wchar_t) c);
1354 switch (width) {
1355 case 2:
5a73255e 1356 *cpos++ = c | curr_attr;
1357 if (++curs.x == cols) {
1358 *cpos |= LATTR_WRAPPED;
1359 if (curs.y == marg_b)
1360 scroll(marg_t, marg_b, 1, TRUE);
1361 else if (curs.y < rows - 1)
1362 curs.y++;
1363 curs.x = 0;
1364 fix_cpos;
4eeb7d09 1365 }
5a73255e 1366 *cpos++ = UCSWIDE | curr_attr;
1367 break;
4eeb7d09 1368 case 1:
1369 *cpos++ = c | curr_attr;
32874aea 1370 break;
4eeb7d09 1371 default:
1372 continue;
32874aea 1373 }
374330e2 1374 }
32874aea 1375 curs.x++;
1376 if (curs.x == cols) {
1377 cpos--;
1378 curs.x--;
1379 wrapnext = TRUE;
4eeb7d09 1380 if (wrap && vt52_mode) {
1381 cpos[1] |= LATTR_WRAPPED;
1382 if (curs.y == marg_b)
1383 scroll(marg_t, marg_b, 1, TRUE);
1384 else if (curs.y < rows - 1)
1385 curs.y++;
1386 curs.x = 0;
1387 fix_cpos;
1388 wrapnext = FALSE;
1389 }
e14a5a13 1390 }
32874aea 1391 seen_disp_event = 1;
374330e2 1392 break;
32874aea 1393
32874aea 1394 case OSC_MAYBE_ST:
1395 /*
1396 * This state is virtually identical to SEEN_ESC, with the
1397 * exception that we have an OSC sequence in the pipeline,
1398 * and _if_ we see a backslash, we process it.
1399 */
1400 if (c == '\\') {
1401 do_osc();
1402 termstate = TOPLEVEL;
1403 break;
e14a5a13 1404 }
32874aea 1405 /* else fall through */
1406 case SEEN_ESC:
4eeb7d09 1407 if (c >= ' ' && c <= '/') {
1408 if (esc_query)
1409 esc_query = -1;
1410 else
1411 esc_query = c;
32874aea 1412 break;
4eeb7d09 1413 }
1414 termstate = TOPLEVEL;
1415 switch (ANSI(c, esc_query)) {
32874aea 1416 case '[': /* enter CSI mode */
1417 termstate = SEEN_CSI;
1418 esc_nargs = 1;
1419 esc_args[0] = ARG_DEFAULT;
1420 esc_query = FALSE;
1421 break;
1422 case ']': /* xterm escape sequences */
1423 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1424 compatibility(OTHER);
1425 termstate = SEEN_OSC;
1426 esc_args[0] = 0;
1427 break;
32874aea 1428 case '7': /* save cursor */
1429 compatibility(VT100);
1430 save_cursor(TRUE);
1431 break;
1432 case '8': /* restore cursor */
1433 compatibility(VT100);
1434 save_cursor(FALSE);
1435 seen_disp_event = TRUE;
1436 break;
1437 case '=':
1438 compatibility(VT100);
1439 app_keypad_keys = TRUE;
1440 break;
1441 case '>':
1442 compatibility(VT100);
1443 app_keypad_keys = FALSE;
1444 break;
1445 case 'D': /* exactly equivalent to LF */
1446 compatibility(VT100);
1447 if (curs.y == marg_b)
1448 scroll(marg_t, marg_b, 1, TRUE);
1449 else if (curs.y < rows - 1)
1450 curs.y++;
1451 fix_cpos;
1452 wrapnext = FALSE;
1453 seen_disp_event = TRUE;
1454 break;
1455 case 'E': /* exactly equivalent to CR-LF */
1456 compatibility(VT100);
1457 curs.x = 0;
1458 if (curs.y == marg_b)
1459 scroll(marg_t, marg_b, 1, TRUE);
1460 else if (curs.y < rows - 1)
1461 curs.y++;
1462 fix_cpos;
1463 wrapnext = FALSE;
1464 seen_disp_event = TRUE;
1465 break;
1466 case 'M': /* reverse index - backwards LF */
1467 compatibility(VT100);
1468 if (curs.y == marg_t)
1469 scroll(marg_t, marg_b, -1, TRUE);
1470 else if (curs.y > 0)
1471 curs.y--;
1472 fix_cpos;
1473 wrapnext = FALSE;
1474 seen_disp_event = TRUE;
1475 break;
1476 case 'Z': /* terminal type query */
1477 compatibility(VT100);
1478 ldisc_send(id_string, strlen(id_string));
1479 break;
1480 case 'c': /* restore power-on settings */
1481 compatibility(VT100);
1482 power_on();
1483 if (reset_132) {
5a73255e 1484 request_resize(80, rows);
32874aea 1485 reset_132 = 0;
374330e2 1486 }
32874aea 1487 fix_cpos;
1488 disptop = 0;
1489 seen_disp_event = TRUE;
1490 break;
32874aea 1491 case 'H': /* set a tab */
1492 compatibility(VT100);
1493 tabs[curs.x] = TRUE;
1494 break;
4eeb7d09 1495
1496 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1497 compatibility(VT100);
1498 {
1499 unsigned long *ldata;
1500 int i, j;
1501 pos scrtop, scrbot;
1502
1503 for (i = 0; i < rows; i++) {
1504 ldata = lineptr(i);
1505 for (j = 0; j < cols; j++)
1506 ldata[j] = ATTR_DEFAULT | 'E';
1507 ldata[cols] = 0;
1508 }
1509 disptop = 0;
1510 seen_disp_event = TRUE;
1511 scrtop.x = scrtop.y = 0;
1512 scrbot.x = 0;
1513 scrbot.y = rows;
1514 check_selection(scrtop, scrbot);
1515 }
1516 break;
1517
1518 case ANSI('3', '#'):
1519 case ANSI('4', '#'):
1520 case ANSI('5', '#'):
1521 case ANSI('6', '#'):
1522 compatibility(VT100);
1523 {
1524 unsigned long nlattr;
1525 unsigned long *ldata;
1526 switch (ANSI(c, esc_query)) {
1527 case ANSI('3', '#'):
1528 nlattr = LATTR_TOP;
1529 break;
1530 case ANSI('4', '#'):
1531 nlattr = LATTR_BOT;
1532 break;
1533 case ANSI('5', '#'):
1534 nlattr = LATTR_NORM;
1535 break;
2d466ffd 1536 default: /* spiritually case ANSI('6', '#'): */
4eeb7d09 1537 nlattr = LATTR_WIDE;
1538 break;
1539 }
1540 ldata = lineptr(curs.y);
1541 ldata[cols] &= ~LATTR_MODE;
1542 ldata[cols] |= nlattr;
1543 }
1544 break;
1545
1546 case ANSI('A', '('):
1547 compatibility(VT100);
1548 cset_attr[0] = ATTR_GBCHR;
1549 break;
1550 case ANSI('B', '('):
1551 compatibility(VT100);
1552 cset_attr[0] = ATTR_ASCII;
1553 break;
1554 case ANSI('0', '('):
1555 compatibility(VT100);
1556 cset_attr[0] = ATTR_LINEDRW;
1557 break;
d3cb5465 1558 case ANSI('U', '('):
1559 compatibility(OTHER);
1560 cset_attr[0] = ATTR_SCOACS;
1561 break;
4eeb7d09 1562
1563 case ANSI('A', ')'):
1564 compatibility(VT100);
1565 cset_attr[1] = ATTR_GBCHR;
1566 break;
1567 case ANSI('B', ')'):
1568 compatibility(VT100);
1569 cset_attr[1] = ATTR_ASCII;
1570 break;
1571 case ANSI('0', ')'):
1572 compatibility(VT100);
1573 cset_attr[1] = ATTR_LINEDRW;
1574 break;
d3cb5465 1575 case ANSI('U', ')'):
1576 compatibility(OTHER);
1577 cset_attr[1] = ATTR_SCOACS;
1578 break;
4eeb7d09 1579
1580 case ANSI('8', '%'): /* Old Linux code */
1581 case ANSI('G', '%'):
1582 compatibility(OTHER);
1583 utf = 1;
1584 break;
1585 case ANSI('@', '%'):
1586 compatibility(OTHER);
8f22582c 1587 utf = 0;
4eeb7d09 1588 break;
374330e2 1589 }
1590 break;
32874aea 1591 case SEEN_CSI:
1592 termstate = TOPLEVEL; /* default */
1593 if (isdigit(c)) {
1594 if (esc_nargs <= ARGS_MAX) {
1595 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1596 esc_args[esc_nargs - 1] = 0;
1597 esc_args[esc_nargs - 1] =
1598 10 * esc_args[esc_nargs - 1] + c - '0';
1599 }
1600 termstate = SEEN_CSI;
1601 } else if (c == ';') {
1602 if (++esc_nargs <= ARGS_MAX)
1603 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1604 termstate = SEEN_CSI;
1605 } else if (c < '@') {
1606 if (esc_query)
1607 esc_query = -1;
1608 else if (c == '?')
1609 esc_query = TRUE;
1610 else
1611 esc_query = c;
1612 termstate = SEEN_CSI;
1613 } else
1614 switch (ANSI(c, esc_query)) {
1615 case 'A': /* move up N lines */
1616 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1617 seen_disp_event = TRUE;
1618 break;
1619 case 'e': /* move down N lines */
1620 compatibility(ANSI);
5442e92f 1621 /* FALLTHROUGH */
32874aea 1622 case 'B':
1623 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1624 seen_disp_event = TRUE;
1625 break;
32874aea 1626 case ANSI('c', '>'): /* report xterm version */
1627 compatibility(OTHER);
1628 /* this reports xterm version 136 so that VIM can
1629 use the drag messages from the mouse reporting */
1630 ldisc_send("\033[>0;136;0c", 11);
1631 break;
5442e92f 1632 case 'a': /* move right N cols */
1633 compatibility(ANSI);
1634 /* FALLTHROUGH */
32874aea 1635 case 'C':
1636 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1637 seen_disp_event = TRUE;
1638 break;
1639 case 'D': /* move left N cols */
1640 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1641 seen_disp_event = TRUE;
1642 break;
1643 case 'E': /* move down N lines and CR */
1644 compatibility(ANSI);
1645 move(0, curs.y + def(esc_args[0], 1), 1);
1646 seen_disp_event = TRUE;
1647 break;
1648 case 'F': /* move up N lines and CR */
1649 compatibility(ANSI);
1650 move(0, curs.y - def(esc_args[0], 1), 1);
1651 seen_disp_event = TRUE;
1652 break;
1653 case 'G':
1654 case '`': /* set horizontal posn */
1655 compatibility(ANSI);
1656 move(def(esc_args[0], 1) - 1, curs.y, 0);
1657 seen_disp_event = TRUE;
1658 break;
1659 case 'd': /* set vertical posn */
1660 compatibility(ANSI);
1661 move(curs.x,
1662 (dec_om ? marg_t : 0) + def(esc_args[0],
1663 1) - 1,
1664 (dec_om ? 2 : 0));
1665 seen_disp_event = TRUE;
1666 break;
1667 case 'H':
1668 case 'f': /* set horz and vert posns at once */
1669 if (esc_nargs < 2)
1670 esc_args[1] = ARG_DEFAULT;
1671 move(def(esc_args[1], 1) - 1,
1672 (dec_om ? marg_t : 0) + def(esc_args[0],
1673 1) - 1,
1674 (dec_om ? 2 : 0));
1675 seen_disp_event = TRUE;
1676 break;
1677 case 'J': /* erase screen or parts of it */
1678 {
1679 unsigned int i = def(esc_args[0], 0) + 1;
1680 if (i > 3)
1681 i = 0;
1682 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1683 }
1684 disptop = 0;
1685 seen_disp_event = TRUE;
1686 break;
1687 case 'K': /* erase line or parts of it */
1688 {
1689 unsigned int i = def(esc_args[0], 0) + 1;
1690 if (i > 3)
1691 i = 0;
1692 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1693 }
1694 seen_disp_event = TRUE;
1695 break;
1696 case 'L': /* insert lines */
1697 compatibility(VT102);
1698 if (curs.y <= marg_b)
1699 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1700 FALSE);
1701 fix_cpos;
1702 seen_disp_event = TRUE;
1703 break;
1704 case 'M': /* delete lines */
1705 compatibility(VT102);
1706 if (curs.y <= marg_b)
1707 scroll(curs.y, marg_b, def(esc_args[0], 1),
1708 TRUE);
1709 fix_cpos;
1710 seen_disp_event = TRUE;
1711 break;
1712 case '@': /* insert chars */
1713 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1714 compatibility(VT102);
1715 insch(def(esc_args[0], 1));
1716 seen_disp_event = TRUE;
1717 break;
1718 case 'P': /* delete chars */
1719 compatibility(VT102);
1720 insch(-def(esc_args[0], 1));
1721 seen_disp_event = TRUE;
1722 break;
1723 case 'c': /* terminal type query */
1724 compatibility(VT100);
1725 /* This is the response for a VT102 */
1726 ldisc_send(id_string, strlen(id_string));
1727 break;
1728 case 'n': /* cursor position query */
1729 if (esc_args[0] == 6) {
1730 char buf[32];
1731 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1732 curs.x + 1);
1733 ldisc_send(buf, strlen(buf));
1734 } else if (esc_args[0] == 5) {
1735 ldisc_send("\033[0n", 4);
1736 }
1737 break;
1738 case 'h': /* toggle modes to high */
1739 case ANSI_QUE('h'):
1740 compatibility(VT100);
1741 {
1742 int i;
1743 for (i = 0; i < esc_nargs; i++)
1744 toggle_mode(esc_args[i], esc_query, TRUE);
1745 }
1746 break;
1747 case 'l': /* toggle modes to low */
1748 case ANSI_QUE('l'):
1749 compatibility(VT100);
1750 {
1751 int i;
1752 for (i = 0; i < esc_nargs; i++)
1753 toggle_mode(esc_args[i], esc_query, FALSE);
1754 }
1755 break;
1756 case 'g': /* clear tabs */
1757 compatibility(VT100);
1758 if (esc_nargs == 1) {
1759 if (esc_args[0] == 0) {
1760 tabs[curs.x] = FALSE;
1761 } else if (esc_args[0] == 3) {
1762 int i;
1763 for (i = 0; i < cols; i++)
1764 tabs[i] = FALSE;
1765 }
1766 }
1767 break;
1768 case 'r': /* set scroll margins */
1769 compatibility(VT100);
1770 if (esc_nargs <= 2) {
1771 int top, bot;
1772 top = def(esc_args[0], 1) - 1;
1773 bot = (esc_nargs <= 1
1774 || esc_args[1] ==
1775 0 ? rows : def(esc_args[1], rows)) - 1;
1776 if (bot >= rows)
1777 bot = rows - 1;
1778 /* VTTEST Bug 9 - if region is less than 2 lines
1779 * don't change region.
1780 */
1781 if (bot - top > 0) {
1782 marg_t = top;
1783 marg_b = bot;
1784 curs.x = 0;
1785 /*
1786 * I used to think the cursor should be
1787 * placed at the top of the newly marginned
1788 * area. Apparently not: VMS TPU falls over
1789 * if so.
1790 *
1791 * Well actually it should for Origin mode - RDB
1792 */
1793 curs.y = (dec_om ? marg_t : 0);
1794 fix_cpos;
1795 seen_disp_event = TRUE;
1796 }
1797 }
1798 break;
1799 case 'm': /* set graphics rendition */
1800 {
1801 /*
1802 * A VT100 without the AVO only had one attribute, either
1803 * underline or reverse video depending on the cursor type,
1804 * this was selected by CSI 7m.
1805 *
1806 * case 2:
4eeb7d09 1807 * This is sometimes DIM, eg on the GIGI and Linux
32874aea 1808 * case 8:
4eeb7d09 1809 * This is sometimes INVIS various ANSI.
32874aea 1810 * case 21:
1811 * This like 22 disables BOLD, DIM and INVIS
1812 *
1813 * The ANSI colours appear on any terminal that has colour
1814 * (obviously) but the interaction between sgr0 and the
1815 * colours varies but is usually related to the background
1816 * colour erase item.
1817 * The interaction between colour attributes and the mono
1818 * ones is also very implementation dependent.
1819 *
1820 * The 39 and 49 attributes are likely to be unimplemented.
1821 */
1822 int i;
1823 for (i = 0; i < esc_nargs; i++) {
1824 switch (def(esc_args[i], 0)) {
1825 case 0: /* restore defaults */
1826 curr_attr = ATTR_DEFAULT;
1827 break;
1828 case 1: /* enable bold */
1829 compatibility(VT100AVO);
1830 curr_attr |= ATTR_BOLD;
1831 break;
1832 case 21: /* (enable double underline) */
1833 compatibility(OTHER);
1834 case 4: /* enable underline */
1835 compatibility(VT100AVO);
1836 curr_attr |= ATTR_UNDER;
1837 break;
1838 case 5: /* enable blink */
1839 compatibility(VT100AVO);
1840 curr_attr |= ATTR_BLINK;
1841 break;
1842 case 7: /* enable reverse video */
1843 curr_attr |= ATTR_REVERSE;
1844 break;
d3cb5465 1845 case 10: /* SCO acs off */
1846 compatibility(SCOANSI);
1847 sco_acs = 0; break;
1848 case 11: /* SCO acs on */
1849 compatibility(SCOANSI);
1850 sco_acs = 1; break;
1851 case 12: /* SCO acs on flipped */
1852 compatibility(SCOANSI);
1853 sco_acs = 2; break;
32874aea 1854 case 22: /* disable bold */
1855 compatibility2(OTHER, VT220);
1856 curr_attr &= ~ATTR_BOLD;
1857 break;
1858 case 24: /* disable underline */
1859 compatibility2(OTHER, VT220);
1860 curr_attr &= ~ATTR_UNDER;
1861 break;
1862 case 25: /* disable blink */
1863 compatibility2(OTHER, VT220);
1864 curr_attr &= ~ATTR_BLINK;
1865 break;
1866 case 27: /* disable reverse video */
1867 compatibility2(OTHER, VT220);
1868 curr_attr &= ~ATTR_REVERSE;
1869 break;
1870 case 30:
1871 case 31:
1872 case 32:
1873 case 33:
1874 case 34:
1875 case 35:
1876 case 36:
1877 case 37:
1878 /* foreground */
1879 curr_attr &= ~ATTR_FGMASK;
1880 curr_attr |=
1881 (esc_args[i] - 30) << ATTR_FGSHIFT;
1882 break;
1883 case 39: /* default-foreground */
1884 curr_attr &= ~ATTR_FGMASK;
1885 curr_attr |= ATTR_DEFFG;
1886 break;
1887 case 40:
1888 case 41:
1889 case 42:
1890 case 43:
1891 case 44:
1892 case 45:
1893 case 46:
1894 case 47:
1895 /* background */
1896 curr_attr &= ~ATTR_BGMASK;
1897 curr_attr |=
1898 (esc_args[i] - 40) << ATTR_BGSHIFT;
1899 break;
1900 case 49: /* default-background */
1901 curr_attr &= ~ATTR_BGMASK;
1902 curr_attr |= ATTR_DEFBG;
1903 break;
1904 }
1905 }
1906 if (use_bce)
8f22582c 1907 erase_char = (' ' | ATTR_ASCII |
1908 (curr_attr &
1909 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 1910 }
1911 break;
1912 case 's': /* save cursor */
1913 save_cursor(TRUE);
1914 break;
1915 case 'u': /* restore cursor */
1916 save_cursor(FALSE);
1917 seen_disp_event = TRUE;
1918 break;
1919 case 't': /* set page size - ie window height */
374330e2 1920 /*
32874aea 1921 * VT340/VT420 sequence DECSLPP, DEC only allows values
1922 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1923 * illegal values (eg first arg 1..9) for window changing
1924 * and reports.
1925 */
1926 compatibility(VT340TEXT);
1927 if (esc_nargs <= 1
1928 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
5a73255e 1929 request_resize(cols, def(esc_args[0], 24));
32874aea 1930 deselect();
1931 }
1932 break;
1a837633 1933 case 'S':
1934 compatibility(SCOANSI);
1935 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1936 fix_cpos;
1937 wrapnext = FALSE;
1938 seen_disp_event = TRUE;
1939 break;
1940 case 'T':
1941 compatibility(SCOANSI);
1942 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1943 fix_cpos;
1944 wrapnext = FALSE;
1945 seen_disp_event = TRUE;
1946 break;
32874aea 1947 case ANSI('|', '*'):
1948 /* VT420 sequence DECSNLS
1949 * Set number of lines on screen
1950 * VT420 uses VGA like hardware and can support any size in
1951 * reasonable range (24..49 AIUI) with no default specified.
1952 */
1953 compatibility(VT420);
1954 if (esc_nargs == 1 && esc_args[0] > 0) {
5a73255e 1955 request_resize(cols, def(esc_args[0], cfg.height));
32874aea 1956 deselect();
1957 }
1958 break;
1959 case ANSI('|', '$'):
1960 /* VT340/VT420 sequence DECSCPP
1961 * Set number of columns per page
1962 * Docs imply range is only 80 or 132, but I'll allow any.
1963 */
1964 compatibility(VT340TEXT);
1965 if (esc_nargs <= 1) {
5a73255e 1966 request_resize(def(esc_args[0], cfg.width), rows);
32874aea 1967 deselect();
1968 }
1969 break;
1970 case 'X': /* write N spaces w/o moving cursor */
1971 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1972 compatibility(ANSIMIN);
1973 {
1974 int n = def(esc_args[0], 1);
1975 pos cursplus;
1976 unsigned long *p = cpos;
1977 if (n > cols - curs.x)
1978 n = cols - curs.x;
1979 cursplus = curs;
1980 cursplus.x += n;
1981 check_selection(curs, cursplus);
1982 while (n--)
1983 *p++ = erase_char;
1984 seen_disp_event = TRUE;
1985 }
1986 break;
1987 case 'x': /* report terminal characteristics */
1988 compatibility(VT100);
1989 {
1990 char buf[32];
1991 int i = def(esc_args[0], 0);
1992 if (i == 0 || i == 1) {
1993 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1994 buf[2] += i;
1995 ldisc_send(buf, 20);
1996 }
1997 }
1998 break;
979f6987 1999 case 'Z': /* BackTab for xterm */
2000 compatibility(OTHER);
2001 {
2002 int i = def(esc_args[0], 1);
2003 pos old_curs = curs;
2004
2005 for(;i>0 && curs.x>0; i--) {
2006 do {
2007 curs.x--;
2008 } while (curs.x >0 && !tabs[curs.x]);
2009 }
2010 fix_cpos;
2011 check_selection(old_curs, curs);
2012 }
2013 break;
32874aea 2014 case ANSI('L', '='):
2015 compatibility(OTHER);
2016 use_bce = (esc_args[0] <= 0);
2017 erase_char = ERASE_CHAR;
2018 if (use_bce)
8f22582c 2019 erase_char = (' ' | ATTR_ASCII |
2020 (curr_attr &
2021 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 2022 break;
2023 case ANSI('E', '='):
2024 compatibility(OTHER);
2025 blink_is_real = (esc_args[0] >= 1);
2026 break;
2027 case ANSI('p', '"'):
2028 /* Allow the host to make this emulator a 'perfect' VT102.
2029 * This first appeared in the VT220, but we do need to get
2030 * back to PuTTY mode so I won't check it.
e14a5a13 2031 *
4eeb7d09 2032 * The arg in 40..42,50 are a PuTTY extension.
32874aea 2033 * The 2nd arg, 8bit vs 7bit is not checked.
2034 *
2035 * Setting VT102 mode should also change the Fkeys to
2036 * generate PF* codes as a real VT102 has no Fkeys.
2037 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
2038 * send nothing.
2039 *
2040 * Note ESC c will NOT change this!
374330e2 2041 */
32874aea 2042
2043 switch (esc_args[0]) {
2044 case 61:
2045 compatibility_level &= ~TM_VTXXX;
2046 compatibility_level |= TM_VT102;
374330e2 2047 break;
32874aea 2048 case 62:
2049 compatibility_level &= ~TM_VTXXX;
2050 compatibility_level |= TM_VT220;
2051 break;
2052
2053 default:
2054 if (esc_args[0] > 60 && esc_args[0] < 70)
2055 compatibility_level |= TM_VTXXX;
2056 break;
2057
2058 case 40:
2059 compatibility_level &= TM_VTXXX;
374330e2 2060 break;
32874aea 2061 case 41:
2062 compatibility_level = TM_PUTTY;
374330e2 2063 break;
32874aea 2064 case 42:
2065 compatibility_level = TM_SCOANSI;
374330e2 2066 break;
32874aea 2067
2068 case ARG_DEFAULT:
2069 compatibility_level = TM_PUTTY;
2070 break;
2071 case 50:
2072 break;
2073 }
2074
2075 /* Change the response to CSI c */
2076 if (esc_args[0] == 50) {
2077 int i;
2078 char lbuf[64];
2079 strcpy(id_string, "\033[?");
2080 for (i = 1; i < esc_nargs; i++) {
2081 if (i != 1)
2082 strcat(id_string, ";");
2083 sprintf(lbuf, "%d", esc_args[i]);
2084 strcat(id_string, lbuf);
2085 }
2086 strcat(id_string, "c");
2087 }
2088#if 0
2089 /* Is this a good idea ?
2090 * Well we should do a soft reset at this point ...
2091 */
2092 if (!has_compat(VT420) && has_compat(VT100)) {
2093 if (reset_132)
5a73255e 2094 request_resize(132, 24);
32874aea 2095 else
5a73255e 2096 request_resize(80, 24);
374330e2 2097 }
32874aea 2098#endif
2099 break;
374330e2 2100 }
374330e2 2101 break;
32874aea 2102 case SEEN_OSC:
2103 osc_w = FALSE;
2104 switch (c) {
2105 case 'P': /* Linux palette sequence */
2106 termstate = SEEN_OSC_P;
2107 osc_strlen = 0;
2108 break;
2109 case 'R': /* Linux palette reset */
2110 palette_reset();
2111 term_invalidate();
2112 termstate = TOPLEVEL;
2113 break;
2114 case 'W': /* word-set */
2115 termstate = SEEN_OSC_W;
2116 osc_w = TRUE;
2117 break;
2118 case '0':
2119 case '1':
2120 case '2':
2121 case '3':
2122 case '4':
2123 case '5':
2124 case '6':
2125 case '7':
2126 case '8':
2127 case '9':
2128 esc_args[0] = 10 * esc_args[0] + c - '0';
2129 break;
2130 case 'L':
2131 /*
2132 * Grotty hack to support xterm and DECterm title
2133 * sequences concurrently.
2134 */
2135 if (esc_args[0] == 2) {
2136 esc_args[0] = 1;
2137 break;
2138 }
2139 /* else fall through */
2140 default:
2141 termstate = OSC_STRING;
2142 osc_strlen = 0;
e14a5a13 2143 }
2144 break;
32874aea 2145 case OSC_STRING:
2146 /*
2147 * This OSC stuff is EVIL. It takes just one character to get into
2148 * sysline mode and it's not initially obvious how to get out.
2149 * So I've added CR and LF as string aborts.
2150 * This shouldn't effect compatibility as I believe embedded
2151 * control characters are supposed to be interpreted (maybe?)
2152 * and they don't display anything useful anyway.
2153 *
2154 * -- RDB
e14a5a13 2155 */
32874aea 2156 if (c == '\n' || c == '\r') {
2157 termstate = TOPLEVEL;
2158 } else if (c == 0234 || c == '\007') {
2159 /*
2160 * These characters terminate the string; ST and BEL
2161 * terminate the sequence and trigger instant
2162 * processing of it, whereas ESC goes back to SEEN_ESC
2163 * mode unless it is followed by \, in which case it is
2164 * synonymous with ST in the first place.
2165 */
2166 do_osc();
2167 termstate = TOPLEVEL;
2168 } else if (c == '\033')
2169 termstate = OSC_MAYBE_ST;
2170 else if (osc_strlen < OSC_STR_MAX)
2171 osc_string[osc_strlen++] = c;
374330e2 2172 break;
32874aea 2173 case SEEN_OSC_P:
374330e2 2174 {
32874aea 2175 int max = (osc_strlen == 0 ? 21 : 16);
2176 int val;
2177 if (c >= '0' && c <= '9')
2178 val = c - '0';
2179 else if (c >= 'A' && c <= 'A' + max - 10)
2180 val = c - 'A' + 10;
2181 else if (c >= 'a' && c <= 'a' + max - 10)
2182 val = c - 'a' + 10;
2d466ffd 2183 else {
32874aea 2184 termstate = TOPLEVEL;
2d466ffd 2185 break;
2186 }
32874aea 2187 osc_string[osc_strlen++] = val;
2188 if (osc_strlen >= 7) {
2189 palette_set(osc_string[0],
2190 osc_string[1] * 16 + osc_string[2],
2191 osc_string[3] * 16 + osc_string[4],
2192 osc_string[5] * 16 + osc_string[6]);
2193 term_invalidate();
2194 termstate = TOPLEVEL;
374330e2 2195 }
2196 }
2197 break;
32874aea 2198 case SEEN_OSC_W:
2199 switch (c) {
2200 case '0':
2201 case '1':
2202 case '2':
2203 case '3':
2204 case '4':
2205 case '5':
2206 case '6':
2207 case '7':
2208 case '8':
2209 case '9':
2210 esc_args[0] = 10 * esc_args[0] + c - '0';
2211 break;
2212 default:
2213 termstate = OSC_STRING;
2214 osc_strlen = 0;
ec55b220 2215 }
32874aea 2216 break;
32874aea 2217 case VT52_ESC:
374330e2 2218 termstate = TOPLEVEL;
32874aea 2219 seen_disp_event = TRUE;
c9def1b8 2220 switch (c) {
32874aea 2221 case 'A':
2222 move(curs.x, curs.y - 1, 1);
2223 break;
2224 case 'B':
2225 move(curs.x, curs.y + 1, 1);
2226 break;
2227 case 'C':
2228 move(curs.x + 1, curs.y, 1);
2229 break;
2230 case 'D':
2231 move(curs.x - 1, curs.y, 1);
2232 break;
4eeb7d09 2233 /*
2234 * From the VT100 Manual
2235 * NOTE: The special graphics characters in the VT100
2236 * are different from those in the VT52
2237 *
2238 * From VT102 manual:
2239 * 137 _ Blank - Same
2240 * 140 ` Reserved - Humm.
2241 * 141 a Solid rectangle - Similar
2242 * 142 b 1/ - Top half of fraction for the
2243 * 143 c 3/ - subscript numbers below.
2244 * 144 d 5/
2245 * 145 e 7/
2246 * 146 f Degrees - Same
2247 * 147 g Plus or minus - Same
2248 * 150 h Right arrow
2249 * 151 i Ellipsis (dots)
2250 * 152 j Divide by
2251 * 153 k Down arrow
2252 * 154 l Bar at scan 0
2253 * 155 m Bar at scan 1
2254 * 156 n Bar at scan 2
2255 * 157 o Bar at scan 3 - Similar
2256 * 160 p Bar at scan 4 - Similar
2257 * 161 q Bar at scan 5 - Similar
2258 * 162 r Bar at scan 6 - Same
2259 * 163 s Bar at scan 7 - Similar
2260 * 164 t Subscript 0
2261 * 165 u Subscript 1
2262 * 166 v Subscript 2
2263 * 167 w Subscript 3
2264 * 170 x Subscript 4
2265 * 171 y Subscript 5
2266 * 172 z Subscript 6
2267 * 173 { Subscript 7
2268 * 174 | Subscript 8
2269 * 175 } Subscript 9
2270 * 176 ~ Paragraph
2271 *
2272 */
32874aea 2273 case 'F':
2274 cset_attr[cset = 0] = ATTR_LINEDRW;
2275 break;
2276 case 'G':
2277 cset_attr[cset = 0] = ATTR_ASCII;
2278 break;
2279 case 'H':
2280 move(0, 0, 0);
2281 break;
2282 case 'I':
2283 if (curs.y == 0)
2284 scroll(0, rows - 1, -1, TRUE);
2285 else if (curs.y > 0)
2286 curs.y--;
2287 fix_cpos;
2288 wrapnext = FALSE;
2289 break;
2290 case 'J':
2291 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2292 disptop = 0;
c9def1b8 2293 break;
32874aea 2294 case 'K':
2295 erase_lots(TRUE, FALSE, TRUE);
2296 break;
4eeb7d09 2297#if 0
32874aea 2298 case 'V':
2299 /* XXX Print cursor line */
2300 break;
2301 case 'W':
2302 /* XXX Start controller mode */
2303 break;
2304 case 'X':
2305 /* XXX Stop controller mode */
2306 break;
4eeb7d09 2307#endif
32874aea 2308 case 'Y':
2309 termstate = VT52_Y1;
2310 break;
2311 case 'Z':
2312 ldisc_send("\033/Z", 3);
2313 break;
2314 case '=':
2315 app_keypad_keys = TRUE;
2316 break;
2317 case '>':
2318 app_keypad_keys = FALSE;
2319 break;
2320 case '<':
2321 /* XXX This should switch to VT100 mode not current or default
2322 * VT mode. But this will only have effect in a VT220+
2323 * emulation.
2324 */
2325 vt52_mode = FALSE;
4eeb7d09 2326 blink_is_real = cfg.blinktext;
32874aea 2327 break;
4eeb7d09 2328#if 0
32874aea 2329 case '^':
2330 /* XXX Enter auto print mode */
2331 break;
2332 case '_':
2333 /* XXX Exit auto print mode */
2334 break;
2335 case ']':
2336 /* XXX Print screen */
2337 break;
4eeb7d09 2338#endif
2339
2340#ifdef VT52_PLUS
2341 case 'E':
2342 /* compatibility(ATARI) */
2343 move(0, 0, 0);
2344 erase_lots(FALSE, FALSE, TRUE);
2345 disptop = 0;
2346 break;
2347 case 'L':
2348 /* compatibility(ATARI) */
2349 if (curs.y <= marg_b)
2350 scroll(curs.y, marg_b, -1, FALSE);
2351 break;
2352 case 'M':
2353 /* compatibility(ATARI) */
2354 if (curs.y <= marg_b)
2355 scroll(curs.y, marg_b, 1, TRUE);
2356 break;
2357 case 'b':
2358 /* compatibility(ATARI) */
2359 termstate = VT52_FG;
2360 break;
2361 case 'c':
2362 /* compatibility(ATARI) */
2363 termstate = VT52_BG;
2364 break;
2365 case 'd':
2366 /* compatibility(ATARI) */
2367 erase_lots(FALSE, TRUE, FALSE);
2368 disptop = 0;
2369 break;
2370 case 'e':
2371 /* compatibility(ATARI) */
2372 cursor_on = TRUE;
2373 break;
2374 case 'f':
2375 /* compatibility(ATARI) */
2376 cursor_on = FALSE;
2377 break;
2378 /* case 'j': Save cursor position - broken on ST */
2379 /* case 'k': Restore cursor position */
2380 case 'l':
2381 /* compatibility(ATARI) */
2382 erase_lots(TRUE, TRUE, TRUE);
2383 curs.x = 0;
2384 wrapnext = FALSE;
2385 fix_cpos;
2386 break;
2387 case 'o':
2388 /* compatibility(ATARI) */
2389 erase_lots(TRUE, TRUE, FALSE);
2390 break;
2391 case 'p':
2392 /* compatibility(ATARI) */
2393 curr_attr |= ATTR_REVERSE;
2394 break;
2395 case 'q':
2396 /* compatibility(ATARI) */
2397 curr_attr &= ~ATTR_REVERSE;
2398 break;
2399 case 'v': /* wrap Autowrap on - Wyse style */
2400 /* compatibility(ATARI) */
2401 wrap = 1;
2402 break;
2403 case 'w': /* Autowrap off */
2404 /* compatibility(ATARI) */
2405 wrap = 0;
2406 break;
2407
2408 case 'R':
2409 /* compatibility(OTHER) */
2410 vt52_bold = FALSE;
2411 curr_attr = ATTR_DEFAULT;
2412 if (use_bce)
8f22582c 2413 erase_char = (' ' | ATTR_ASCII |
2414 (curr_attr &
2415 (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2416 break;
2417 case 'S':
2418 /* compatibility(VI50) */
2419 curr_attr |= ATTR_UNDER;
2420 break;
2421 case 'W':
2422 /* compatibility(VI50) */
2423 curr_attr &= ~ATTR_UNDER;
2424 break;
2425 case 'U':
2426 /* compatibility(VI50) */
2427 vt52_bold = TRUE;
2428 curr_attr |= ATTR_BOLD;
2429 break;
2430 case 'T':
2431 /* compatibility(VI50) */
2432 vt52_bold = FALSE;
2433 curr_attr &= ~ATTR_BOLD;
2434 break;
2435#endif
c9def1b8 2436 }
e14a5a13 2437 break;
32874aea 2438 case VT52_Y1:
2439 termstate = VT52_Y2;
2440 move(curs.x, c - ' ', 0);
e14a5a13 2441 break;
32874aea 2442 case VT52_Y2:
2443 termstate = TOPLEVEL;
2444 move(c - ' ', curs.y, 0);
e14a5a13 2445 break;
4eeb7d09 2446
2447#ifdef VT52_PLUS
2448 case VT52_FG:
2449 termstate = TOPLEVEL;
2450 curr_attr &= ~ATTR_FGMASK;
2451 curr_attr &= ~ATTR_BOLD;
2452 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2453 if ((c & 0x8) || vt52_bold)
2454 curr_attr |= ATTR_BOLD;
2455
2456 if (use_bce)
8f22582c 2457 erase_char = (' ' | ATTR_ASCII |
2458 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2459 break;
2460 case VT52_BG:
2461 termstate = TOPLEVEL;
2462 curr_attr &= ~ATTR_BGMASK;
2463 curr_attr &= ~ATTR_BLINK;
2464 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2465
2466 /* Note: bold background */
2467 if (c & 0x8)
2468 curr_attr |= ATTR_BLINK;
2469
2470 if (use_bce)
8f22582c 2471 erase_char = (' ' | ATTR_ASCII |
2472 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2473 break;
2474#endif
2d466ffd 2475 default: break; /* placate gcc warning about enum use */
e14a5a13 2476 }
4facdf84 2477 if (selstate != NO_SELECTION) {
2478 pos cursplus = curs;
2479 incpos(cursplus);
32874aea 2480 check_selection(curs, cursplus);
4facdf84 2481 }
374330e2 2482 }
c9def1b8 2483 inbuf_head = 0;
374330e2 2484}
2485
4eeb7d09 2486#if 0
374330e2 2487/*
2488 * Compare two lines to determine whether they are sufficiently
2489 * alike to scroll-optimise one to the other. Return the degree of
2490 * similarity.
2491 */
32874aea 2492static int linecmp(unsigned long *a, unsigned long *b)
2493{
374330e2 2494 int i, n;
2495
32874aea 2496 for (i = n = 0; i < cols; i++)
374330e2 2497 n += (*a++ == *b++);
2498 return n;
2499}
4eeb7d09 2500#endif
374330e2 2501
2502/*
2503 * Given a context, update the window. Out of paranoia, we don't
2504 * allow WM_PAINT responses to do scrolling optimisations.
2505 */
32874aea 2506static void do_paint(Context ctx, int may_optimise)
2507{
4eeb7d09 2508 int i, j, our_curs_y;
2509 unsigned long rv, cursor;
4facdf84 2510 pos scrpos;
374330e2 2511 char ch[1024];
4eeb7d09 2512 long cursor_background = ERASE_CHAR;
156686ef 2513 long ticks;
374330e2 2514
156686ef 2515 /*
2516 * Check the visual bell state.
2517 */
2518 if (in_vbell) {
2519 ticks = GetTickCount();
2520 if (ticks - vbell_timeout >= 0)
2521 in_vbell = FALSE;
2522 }
2523
4eeb7d09 2524 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2525
156686ef 2526 /* Depends on:
2527 * screen array, disptop, scrtop,
2528 * selection, rv,
2529 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2530 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2531 */
4eeb7d09 2532
2533 /* Has the cursor position or type changed ? */
e14a5a13 2534 if (cursor_on) {
32874aea 2535 if (has_focus) {
217dceef 2536 if (blinker || !cfg.blink_cur)
4eeb7d09 2537 cursor = TATTR_ACTCURS;
32874aea 2538 else
2539 cursor = 0;
2540 } else
4eeb7d09 2541 cursor = TATTR_PASCURS;
4e30ff69 2542 if (wrapnext)
4eeb7d09 2543 cursor |= TATTR_RIGHTCURS;
32874aea 2544 } else
156686ef 2545 cursor = 0;
4facdf84 2546 our_curs_y = curs.y - disptop;
374330e2 2547
4eeb7d09 2548 if (dispcurs && (curstype != cursor ||
2549 dispcurs !=
2550 disptext + our_curs_y * (cols + 1) + curs.x)) {
5a73255e 2551 if (dispcurs > disptext &&
2552 (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
4eeb7d09 2553 dispcurs[-1] |= ATTR_INVALID;
5a73255e 2554 if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
4eeb7d09 2555 dispcurs[1] |= ATTR_INVALID;
2556 *dispcurs |= ATTR_INVALID;
2557 curstype = 0;
2558 }
2559 dispcurs = NULL;
2560
2561 /* The normal screen data */
32874aea 2562 for (i = 0; i < rows; i++) {
4facdf84 2563 unsigned long *ldata;
2564 int lattr;
4eeb7d09 2565 int idx, dirty_line, dirty_run;
2566 unsigned long attr = 0;
2567 int updated_line = 0;
2568 int start = 0;
2569 int ccount = 0;
2570 int last_run_dirty = 0;
2571
4facdf84 2572 scrpos.y = i + disptop;
2573 ldata = lineptr(scrpos.y);
2574 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2575
2576 idx = i * (cols + 1);
2577 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2578 disptext[idx + cols] = ldata[cols];
2579
2580 for (j = 0; j < cols; j++, idx++) {
2581 unsigned long tattr, tchar;
2582 unsigned long *d = ldata + j;
2583 int break_run;
4facdf84 2584 scrpos.x = j;
32874aea 2585
4eeb7d09 2586 tchar = (*d & (CHAR_MASK | CSET_MASK));
2587 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2588 switch (tchar & CSET_MASK) {
2589 case ATTR_ASCII:
2590 tchar = unitab_line[tchar & 0xFF];
2591 break;
2592 case ATTR_LINEDRW:
2593 tchar = unitab_xterm[tchar & 0xFF];
2594 break;
d3cb5465 2595 case ATTR_SCOACS:
2596 tchar = unitab_scoacs[tchar&0xFF];
2597 break;
4eeb7d09 2598 }
2599 tattr |= (tchar & CSET_MASK);
2600 tchar &= CHAR_MASK;
5a73255e 2601 if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
2602 tattr |= ATTR_WIDE;
4eeb7d09 2603
2604 /* Video reversing things */
2605 tattr = (tattr ^ rv
2606 ^ (posle(selstart, scrpos) &&
2607 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2608
2609 /* 'Real' blinking ? */
2610 if (blink_is_real && (tattr & ATTR_BLINK)) {
2611 if (has_focus && tblinker) {
2612 tchar = ' ';
2613 tattr &= ~CSET_MASK;
2614 tattr |= ATTR_ACP;
c9def1b8 2615 }
4eeb7d09 2616 tattr &= ~ATTR_BLINK;
c9def1b8 2617 }
374330e2 2618
5a73255e 2619 /*
2620 * Check the font we'll _probably_ be using to see if
2621 * the character is wide when we don't want it to be.
2622 */
2623 if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
2624 if ((tattr & ATTR_WIDE) == 0 &&
2625 CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
2626 tattr |= ATTR_NARROW;
2627 } else if (disptext[idx]&ATTR_NARROW)
2628 tattr |= ATTR_NARROW;
2629
4eeb7d09 2630 /* Cursor here ? Save the 'background' */
2631 if (i == our_curs_y && j == curs.x) {
2632 cursor_background = tattr | tchar;
2633 dispcurs = disptext + idx;
2634 }
374330e2 2635
4eeb7d09 2636 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2637 dirty_line = TRUE;
2638
2639 break_run = (tattr != attr || j - start >= sizeof(ch));
2640
2641 /* Special hack for VT100 Linedraw glyphs */
2642 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2643 && tchar <= 0xBD) break_run = TRUE;
2644
2645 if (!dbcs_screenfont && !dirty_line) {
2646 if ((tchar | tattr) == disptext[idx])
2647 break_run = TRUE;
2648 else if (!dirty_run && ccount == 1)
2649 break_run = TRUE;
374330e2 2650 }
4eeb7d09 2651
2652 if (break_run) {
2653 if ((dirty_run || last_run_dirty) && ccount > 0) {
2654 do_text(ctx, start, i, ch, ccount, attr, lattr);
2655 updated_line = 1;
2656 }
2657 start = j;
2658 ccount = 0;
2659 attr = tattr;
2660 if (dbcs_screenfont)
2661 last_run_dirty = dirty_run;
2662 dirty_run = dirty_line;
2663 }
2664
2665 if ((tchar | tattr) != disptext[idx])
2666 dirty_run = TRUE;
2667 ch[ccount++] = (char) tchar;
2668 disptext[idx] = tchar | tattr;
2669
2670 /* If it's a wide char step along to the next one. */
2671 if (tattr & ATTR_WIDE) {
2672 if (++j < cols) {
2673 idx++;
2674 d++;
2675 /* Cursor is here ? Ouch! */
2676 if (i == our_curs_y && j == curs.x) {
2677 cursor_background = *d;
2678 dispcurs = disptext + idx;
2679 }
2680 if (disptext[idx] != *d)
2681 dirty_run = TRUE;
2682 disptext[idx] = *d;
374330e2 2683 }
374330e2 2684 }
4eeb7d09 2685 }
2686 if (dirty_run && ccount > 0) {
2687 do_text(ctx, start, i, ch, ccount, attr, lattr);
2688 updated_line = 1;
2689 }
2690
2691 /* Cursor on this line ? (and changed) */
2692 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2693 ch[0] = (char) (cursor_background & CHAR_MASK);
2694 attr = (cursor_background & ATTR_MASK) | cursor;
2695 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2696 curstype = cursor;
374330e2 2697 }
2698 }
2699}
2700
2701/*
e14a5a13 2702 * Flick the switch that says if blinking things should be shown or hidden.
2703 */
2704
32874aea 2705void term_blink(int flg)
2706{
22dcdc3b 2707 static long last_blink = 0;
2708 static long last_tblink = 0;
e14a5a13 2709 long now, blink_diff;
2710
c9def1b8 2711 now = GetTickCount();
32874aea 2712 blink_diff = now - last_tblink;
c9def1b8 2713
2714 /* Make sure the text blinks no more than 2Hz */
32874aea 2715 if (blink_diff < 0 || blink_diff > 450) {
2716 last_tblink = now;
c9def1b8 2717 tblinker = !tblinker;
2718 }
2719
e14a5a13 2720 if (flg) {
32874aea 2721 blinker = 1;
2722 last_blink = now;
e14a5a13 2723 return;
32874aea 2724 }
e14a5a13 2725
32874aea 2726 blink_diff = now - last_blink;
e14a5a13 2727
22dcdc3b 2728 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2729 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2730 return;
2731
e14a5a13 2732 last_blink = now;
2733 blinker = !blinker;
2734}
2735
2736/*
374330e2 2737 * Invalidate the whole screen so it will be repainted in full.
2738 */
32874aea 2739void term_invalidate(void)
2740{
374330e2 2741 int i;
2742
32874aea 2743 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2744 disptext[i] = ATTR_INVALID;
2745}
2746
2747/*
2748 * Paint the window in response to a WM_PAINT message.
2749 */
5a73255e 2750void term_paint(Context ctx, int left, int top, int right, int bottom)
32874aea 2751{
5a73255e 2752 int i, j;
2753 if (left < 0) left = 0;
2754 if (top < 0) top = 0;
2755 if (right >= cols) right = cols-1;
2756 if (bottom >= rows) bottom = rows-1;
374330e2 2757
32874aea 2758 for (i = top; i <= bottom && i < rows; i++) {
2759 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2760 for (j = left; j <= right && j < cols; j++)
2761 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2762 else
32874aea 2763 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2764 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2765 }
374330e2 2766
e14a5a13 2767 /* This should happen soon enough, also for some reason it sometimes
2768 * fails to actually do anything when re-sizing ... painting the wrong
2769 * window perhaps ?
32874aea 2770 */
5a73255e 2771 if (alt_pressed)
2772 do_paint (ctx, FALSE);
374330e2 2773}
2774
2775/*
2776 * Attempt to scroll the scrollback. The second parameter gives the
2777 * position we want to scroll to; the first is +1 to denote that
2778 * this position is relative to the beginning of the scrollback, -1
2779 * to denote it is relative to the end, and 0 to denote that it is
2780 * relative to the current position.
2781 */
32874aea 2782void term_scroll(int rel, int where)
2783{
4facdf84 2784 int sbtop = -count234(scrollback);
374330e2 2785
32874aea 2786 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2787 if (disptop < sbtop)
2788 disptop = sbtop;
4facdf84 2789 if (disptop > 0)
2790 disptop = 0;
374330e2 2791 update_sbar();
2792 term_update();
2793}
2794
4eeb7d09 2795static void clipme(pos top, pos bottom)
32874aea 2796{
4eeb7d09 2797 wchar_t *workbuf;
2798 wchar_t *wbptr; /* where next char goes within workbuf */
32874aea 2799 int wblen = 0; /* workbuf len */
2800 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2801
4eeb7d09 2802 buflen = 5120; /* Default size */
2803 workbuf = smalloc(buflen * sizeof(wchar_t));
2804 wbptr = workbuf; /* start filling here */
bc1235d4 2805
4facdf84 2806 while (poslt(top, bottom)) {
bc1235d4 2807 int nl = FALSE;
4facdf84 2808 unsigned long *ldata = lineptr(top.y);
260f3dec 2809 pos nlpos;
4facdf84 2810
2811 nlpos.y = top.y;
2812 nlpos.x = cols;
bc1235d4 2813
4eeb7d09 2814 if (!(ldata[cols] & LATTR_WRAPPED)) {
2815 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2816 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2817 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2818 && poslt(top, nlpos))
2819 decpos(nlpos);
4facdf84 2820 if (poslt(nlpos, bottom))
bc1235d4 2821 nl = TRUE;
2822 }
4facdf84 2823 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2824#if 0
2825 char cbuf[16], *p;
2826 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2827#else
2828 wchar_t cbuf[16], *p;
2829 int uc = (ldata[top.x] & 0xFFFF);
2830 int set, c;
2831
2832 if (uc == UCSWIDE) {
2833 top.x++;
2834 continue;
2835 }
2836
2837 switch (uc & CSET_MASK) {
2838 case ATTR_LINEDRW:
2839 if (!cfg.rawcnp) {
2840 uc = unitab_xterm[uc & 0xFF];
2841 break;
32874aea 2842 }
4eeb7d09 2843 case ATTR_ASCII:
2844 uc = unitab_line[uc & 0xFF];
2845 break;
d3cb5465 2846 case ATTR_SCOACS:
2847 uc = unitab_scoacs[uc&0xFF];
2848 break;
d3a22f79 2849 }
4eeb7d09 2850 switch (uc & CSET_MASK) {
2851 case ATTR_ACP:
2852 uc = unitab_font[uc & 0xFF];
2853 break;
2854 case ATTR_OEMCP:
2855 uc = unitab_oemcp[uc & 0xFF];
2856 break;
2857 }
2858
2859 set = (uc & CSET_MASK);
2860 c = (uc & CHAR_MASK);
2861 cbuf[0] = uc;
2862 cbuf[1] = 0;
2863
2864 if (DIRECT_FONT(uc)) {
2865 if (c >= ' ' && c != 0x7F) {
2866 unsigned char buf[4];
2867 WCHAR wbuf[4];
2868 int rv;
2869 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2870 buf[0] = c;
2871 buf[1] = (unsigned char) ldata[top.x + 1];
2872 rv = MultiByteToWideChar(font_codepage,
2873 0, buf, 2, wbuf, 4);
2874 top.x++;
2875 } else {
2876 buf[0] = c;
2877 rv = MultiByteToWideChar(font_codepage,
2878 0, buf, 1, wbuf, 4);
2879 }
bc1235d4 2880
4eeb7d09 2881 if (rv > 0) {
2882 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2883 cbuf[rv] = 0;
d3a22f79 2884 }
d3a22f79 2885 }
4eeb7d09 2886 }
2887#endif
2888
2889 for (p = cbuf; *p; p++) {
2890 /* Enough overhead for trailing NL and nul */
2891 if (wblen >= buflen - 16) {
2892 workbuf =
2893 srealloc(workbuf,
2894 sizeof(wchar_t) * (buflen += 100));
2895 wbptr = workbuf + wblen;
2896 }
2897 wblen++;
2898 *wbptr++ = *p;
bc1235d4 2899 }
4facdf84 2900 top.x++;
bc1235d4 2901 }
2902 if (nl) {
2903 int i;
4eeb7d09 2904 for (i = 0; i < sel_nl_sz; i++) {
32874aea 2905 wblen++;
bc1235d4 2906 *wbptr++ = sel_nl[i];
2907 }
2908 }
4facdf84 2909 top.y++;
2910 top.x = 0;
bc1235d4 2911 }
4eeb7d09 2912 wblen++;
2913 *wbptr++ = 0;
32874aea 2914 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2915 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 2916 sfree(workbuf);
bc1235d4 2917}
4eeb7d09 2918
32874aea 2919void term_copyall(void)
2920{
4facdf84 2921 pos top;
2922 top.y = -count234(scrollback);
2923 top.x = 0;
4eeb7d09 2924 clipme(top, curs);
2925}
2926
2927/*
2928 * The wordness array is mainly for deciding the disposition of the US-ASCII
2929 * characters.
2930 */
2931static int wordtype(int uc)
2932{
2933 static struct {
2934 int start, end, ctype;
2935 } *wptr, ucs_words[] = {
2936 {
2937 128, 160, 0}, {
2938 161, 191, 1}, {
2939 215, 215, 1}, {
2940 247, 247, 1}, {
2941 0x037e, 0x037e, 1}, /* Greek question mark */
2942 {
2943 0x0387, 0x0387, 1}, /* Greek ano teleia */
2944 {
2945 0x055a, 0x055f, 1}, /* Armenian punctuation */
2946 {
2947 0x0589, 0x0589, 1}, /* Armenian full stop */
2948 {
2949 0x0700, 0x070d, 1}, /* Syriac punctuation */
2950 {
2951 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2952 {
2953 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2954 {
2955 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2956 {
2957 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2958 {
2959 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2960 {
2961 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2962 {
2963 0x2000, 0x200a, 0}, /* Various spaces */
2964 {
2965 0x2070, 0x207f, 2}, /* superscript */
2966 {
2967 0x2080, 0x208f, 2}, /* subscript */
2968 {
2969 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2970 {
2971 0x3000, 0x3000, 0}, /* ideographic space */
2972 {
2973 0x3001, 0x3020, 1}, /* ideographic punctuation */
2974 {
2975 0x303f, 0x309f, 3}, /* Hiragana */
2976 {
2977 0x30a0, 0x30ff, 3}, /* Katakana */
2978 {
2979 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2980 {
2981 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2982 {
2983 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2984 {
2985 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2986 {
2987 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2988 {
2989 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2990 {
2991 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2992 {
2993 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2994 {
2995 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2996 {
2997 0, 0, 0}
2998 };
2999
3000 uc &= (CSET_MASK | CHAR_MASK);
3001
3002 switch (uc & CSET_MASK) {
3003 case ATTR_LINEDRW:
3004 uc = unitab_xterm[uc & 0xFF];
3005 break;
3006 case ATTR_ASCII:
3007 uc = unitab_line[uc & 0xFF];
3008 break;
d3cb5465 3009 case ATTR_SCOACS:
3010 uc = unitab_scoacs[uc&0xFF];
3011 break;
4eeb7d09 3012 }
3013 switch (uc & CSET_MASK) {
3014 case ATTR_ACP:
3015 uc = unitab_font[uc & 0xFF];
3016 break;
3017 case ATTR_OEMCP:
3018 uc = unitab_oemcp[uc & 0xFF];
3019 break;
3020 }
3021
5a73255e 3022 /* For DBCS font's I can't do anything usefull. Even this will sometimes
3023 * fail as there's such a thing as a double width space. :-(
3024 */
3025 if (dbcs_screenfont && font_codepage == line_codepage)
3026 return (uc != ' ');
3027
4eeb7d09 3028 if (uc < 0x80)
3029 return wordness[uc];
3030
3031 for (wptr = ucs_words; wptr->start; wptr++) {
3032 if (uc >= wptr->start && uc <= wptr->end)
3033 return wptr->ctype;
3034 }
3035
3036 return 2;
bc1235d4 3037}
3038
374330e2 3039/*
3040 * Spread the selection outwards according to the selection mode.
3041 */
32874aea 3042static pos sel_spread_half(pos p, int dir)
3043{
4facdf84 3044 unsigned long *ldata;
374330e2 3045 short wvalue;
3046
4facdf84 3047 ldata = lineptr(p.y);
374330e2 3048
3049 switch (selmode) {
3050 case SM_CHAR:
3051 /*
3052 * In this mode, every character is a separate unit, except
3053 * for runs of spaces at the end of a non-wrapping line.
3054 */
4eeb7d09 3055 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 3056 unsigned long *q = ldata + cols;
4facdf84 3057 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 3058 q--;
32874aea 3059 if (q == ldata + cols)
374330e2 3060 q--;
32874aea 3061 if (p.x >= q - ldata)
3062 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 3063 }
3064 break;
3065 case SM_WORD:
3066 /*
3067 * In this mode, the units are maximal runs of characters
3068 * whose `wordness' has the same value.
3069 */
4eeb7d09 3070 wvalue = wordtype(ldata[p.x]);
374330e2 3071 if (dir == +1) {
4eeb7d09 3072 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3073 p.x++;
374330e2 3074 } else {
4eeb7d09 3075 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3076 p.x--;
374330e2 3077 }
3078 break;
3079 case SM_LINE:
3080 /*
3081 * In this mode, every line is a unit.
3082 */
4facdf84 3083 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 3084 break;
3085 }
3086 return p;
3087}
3088
32874aea 3089static void sel_spread(void)
3090{
3091 selstart = sel_spread_half(selstart, -1);
4facdf84 3092 decpos(selend);
32874aea 3093 selend = sel_spread_half(selend, +1);
4facdf84 3094 incpos(selend);
374330e2 3095}
3096
568dd02f 3097void term_do_paste(void)
3098{
3099 wchar_t *data;
3100 int len;
3101
3102 get_clip(&data, &len);
3103 if (data) {
3104 wchar_t *p, *q;
3105
3106 if (paste_buffer)
3107 sfree(paste_buffer);
3108 paste_pos = paste_hold = paste_len = 0;
3109 paste_buffer = smalloc(len * sizeof(wchar_t));
3110
3111 p = q = data;
3112 while (p < data + len) {
3113 while (p < data + len &&
3114 !(p <= data + len - sel_nl_sz &&
3115 !memcmp(p, sel_nl, sizeof(sel_nl))))
3116 p++;
3117
3118 {
3119 int i;
3120 for (i = 0; i < p - q; i++) {
3121 paste_buffer[paste_len++] = q[i];
3122 }
3123 }
3124
3125 if (p <= data + len - sel_nl_sz &&
3126 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3127 paste_buffer[paste_len++] = '\r';
3128 p += sel_nl_sz;
3129 }
3130 q = p;
3131 }
3132
3133 /* Assume a small paste will be OK in one go. */
3134 if (paste_len < 256) {
3135 luni_send(paste_buffer, paste_len);
3136 if (paste_buffer)
3137 sfree(paste_buffer);
3138 paste_buffer = 0;
3139 paste_pos = paste_hold = paste_len = 0;
3140 }
3141 }
3142 get_clip(NULL, NULL);
3143}
3144
32874aea 3145void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3146 int shift, int ctrl)
3147{
4facdf84 3148 pos selpoint;
3149 unsigned long *ldata;
fff61f25 3150 int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
32874aea 3151
fff61f25 3152 if (y < 0) {
32874aea 3153 y = 0;
fff61f25 3154 if (a == MA_DRAG && !raw_mouse)
3155 term_scroll(0, -1);
3156 }
3157 if (y >= rows) {
32874aea 3158 y = rows - 1;
fff61f25 3159 if (a == MA_DRAG && !raw_mouse)
3160 term_scroll(0, +1);
3161 }
32874aea 3162 if (x < 0) {
3163 if (y > 0) {
3164 x = cols - 1;
3165 y--;
3166 } else
3167 x = 0;
094ed2a6 3168 }
32874aea 3169 if (x >= cols)
3170 x = cols - 1;
37508af4 3171
4facdf84 3172 selpoint.y = y + disptop;
3173 selpoint.x = x;
3174 ldata = lineptr(selpoint.y);
32874aea 3175 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 3176 selpoint.x /= 2;
374330e2 3177
fff61f25 3178 if (raw_mouse) {
01c034ad 3179 int encstate = 0, r, c;
3180 char abuf[16];
3181 static int is_down = 0;
3182
32874aea 3183 switch (b) {
01c034ad 3184 case MBT_LEFT:
32874aea 3185 encstate = 0x20; /* left button down */
01c034ad 3186 break;
3187 case MBT_MIDDLE:
3188 encstate = 0x21;
3189 break;
3190 case MBT_RIGHT:
3191 encstate = 0x22;
3192 break;
3193 case MBT_WHEEL_UP:
3194 encstate = 0x60;
3195 break;
3196 case MBT_WHEEL_DOWN:
3197 encstate = 0x61;
3198 break;
2d466ffd 3199 default: break; /* placate gcc warning about enum use */
01c034ad 3200 }
32874aea 3201 switch (a) {
01c034ad 3202 case MA_DRAG:
3203 if (xterm_mouse == 1)
3204 return;
3205 encstate += 0x20;
3206 break;
3207 case MA_RELEASE:
3208 encstate = 0x23;
3209 is_down = 0;
3210 break;
3211 case MA_CLICK:
3212 if (is_down == b)
3213 return;
3214 is_down = b;
3215 break;
2d466ffd 3216 default: break; /* placate gcc warning about enum use */
01c034ad 3217 }
3218 if (shift)
3219 encstate += 0x04;
3220 if (ctrl)
3221 encstate += 0x10;
3222 r = y + 33;
3223 c = x + 33;
3224
3225 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3226 ldisc_send(abuf, 6);
3227 return;
3228 }
3229
3230 b = translate_button(b);
3231
3232 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3233 deselect();
3234 selstate = ABOUT_TO;
3235 selanchor = selpoint;
3236 selmode = SM_CHAR;
01c034ad 3237 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3238 deselect();
3239 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3240 selstate = DRAGGING;
3241 selstart = selanchor = selpoint;
4facdf84 3242 selend = selstart;
3243 incpos(selend);
374330e2 3244 sel_spread();
01c034ad 3245 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3246 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3247 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3248 return;
01c034ad 3249 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
32874aea 3250 if (posdiff(selpoint, selstart) <
3251 posdiff(selend, selstart) / 2) {
4facdf84 3252 selanchor = selend;
3253 decpos(selanchor);
3254 } else {
374330e2 3255 selanchor = selstart;
4facdf84 3256 }
374330e2 3257 selstate = DRAGGING;
3258 }
3259 if (selstate != ABOUT_TO && selstate != DRAGGING)
3260 selanchor = selpoint;
3261 selstate = DRAGGING;
4facdf84 3262 if (poslt(selpoint, selanchor)) {
374330e2 3263 selstart = selpoint;
4facdf84 3264 selend = selanchor;
3265 incpos(selend);
374330e2 3266 } else {
3267 selstart = selanchor;
4facdf84 3268 selend = selpoint;
a4450583 3269 incpos(selend);
374330e2 3270 }
3271 sel_spread();
01c034ad 3272 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3273 if (selstate == DRAGGING) {
3274 /*
3275 * We've completed a selection. We now transfer the
3276 * data to the clipboard.
3277 */
4eeb7d09 3278 clipme(selstart, selend);
374330e2 3279 selstate = SELECTED;
3280 } else
3281 selstate = NO_SELECTION;
32874aea 3282 } else if (b == MBT_PASTE
3283 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
568dd02f 3284 term_do_paste();
374330e2 3285 }
3286
3287 term_update();
3288}
3289
32874aea 3290void term_nopaste()
3291{
3292 if (paste_len == 0)
3293 return;
c9def1b8 3294 sfree(paste_buffer);
3295 paste_buffer = 0;
3296 paste_len = 0;
3297}
3298
32874aea 3299void term_paste()
3300{
8df7a775 3301 static long last_paste = 0;
c9def1b8 3302 long now, paste_diff;
3303
32874aea 3304 if (paste_len == 0)
3305 return;
c9def1b8 3306
3307 /* Don't wait forever to paste */
32874aea 3308 if (paste_hold) {
3309 now = GetTickCount();
3310 paste_diff = now - last_paste;
3311 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3312 return;
3313 }
3314 paste_hold = 0;
3315
32874aea 3316 while (paste_pos < paste_len) {
8df7a775 3317 int n = 0;
3318 while (n + paste_pos < paste_len) {
3319 if (paste_buffer[paste_pos + n++] == '\r')
3320 break;
3321 }
4eeb7d09 3322 luni_send(paste_buffer + paste_pos, n);
8df7a775 3323 paste_pos += n;
c9def1b8 3324
8df7a775 3325 if (paste_pos < paste_len) {
c9def1b8 3326 paste_hold = 1;
3327 return;
3328 }
3329 }
3330 sfree(paste_buffer);
3331 paste_buffer = 0;
3332 paste_len = 0;
3333}
3334
32874aea 3335static void deselect(void)
3336{
374330e2 3337 selstate = NO_SELECTION;
4facdf84 3338 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3339}
3340
32874aea 3341void term_deselect(void)
3342{
374330e2 3343 deselect();
3344 term_update();
3345}
fe50e814 3346
32874aea 3347int term_ldisc(int option)
3348{
3349 if (option == LD_ECHO)
3350 return term_echoing;
3351 if (option == LD_EDIT)
3352 return term_editing;
0965bee0 3353 return FALSE;
3354}
3355
fe50e814 3356/*
3357 * from_backend(), to get data from the backend for the terminal.
3358 */
5471d09a 3359int from_backend(int is_stderr, char *data, int len)
32874aea 3360{
fe50e814 3361 while (len--) {
3362 if (inbuf_head >= INBUF_SIZE)
3363 term_out();
3364 inbuf[inbuf_head++] = *data++;
3365 }
5471d09a 3366
3367 /*
3368 * We process all stdout/stderr data immediately we receive it,
3369 * and don't return until it's all gone. Therefore, there's no
3370 * reason at all to return anything other than zero from this
3371 * function.
3372 *
3373 * This is a slightly suboptimal way to deal with SSH2 - in
3374 * principle, the window mechanism would allow us to continue
3375 * to accept data on forwarded ports and X connections even
3376 * while the terminal processing was going slowly - but we
3377 * can't do the 100% right thing without moving the terminal
3378 * processing into a separate thread, and that might hurt
3379 * portability. So we manage stdout buffering the old SSH1 way:
3380 * if the terminal processing goes slowly, the whole SSH
3381 * connection stops accepting data until it's ready.
3382 *
3383 * In practice, I can't imagine this causing serious trouble.
3384 */
3385 return 0;
fe50e814 3386}
e1c8e0ed 3387
3388/*
3389 * Log session traffic.
3390 */
32874aea 3391void logtraffic(unsigned char c, int logmode)
3392{
e1c8e0ed 3393 if (cfg.logtype > 0) {
3394 if (cfg.logtype == logmode) {
3395 /* deferred open file from pgm start? */
32874aea 3396 if (!lgfp)
3397 logfopen();
3398 if (lgfp)
3399 fputc(c, lgfp);
3400 }
e1c8e0ed 3401 }
3402}
3403
66ee282a 3404void settimstr(char *ta, int no_sec);
3405char *subslfcode(char *dest, char *src, char *dstrt);
3406char *stpncpy(char *dst, const char *src, size_t maxlen);
3407char timdatbuf[20];
3408char currlogfilename[FILENAME_MAX];
3409
e1c8e0ed 3410/* open log file append/overwrite mode */
32874aea 3411void logfopen(void)
3412{
e1c8e0ed 3413 char buf[256];
3414 time_t t;
66ee282a 3415 struct tm tm;
e1c8e0ed 3416 char writemod[4];
3417
3418 if (!cfg.logtype)
3419 return;
32874aea 3420 sprintf(writemod, "wb"); /* default to rewrite */
66ee282a 3421
3422 time(&t);
3423 tm = *localtime(&t);
3424
3425 /* substitute special codes in file name */
3426 xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
3427
3428 lgfp = fopen(currlogfilename, "r"); /* file already present? */
e1c8e0ed 3429 if (lgfp) {
3430 int i;
3431 fclose(lgfp);
66ee282a 3432 i = askappend(currlogfilename);
e1c8e0ed 3433 if (i == 1)
3434 writemod[0] = 'a'; /* set append mode */
3435 else if (i == 0) { /* cancelled */
3436 lgfp = NULL;
32874aea 3437 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3438 return;
3439 }
3440 }
3441
66ee282a 3442 lgfp = fopen(currlogfilename, writemod);
32874aea 3443 if (lgfp) { /* enter into event log */
e1c8e0ed 3444 sprintf(buf, "%s session log (%s mode) to file : ",
3445 (writemod[0] == 'a') ? "Appending" : "Writing new",
3446 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3447 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3448 /* Make sure we do not exceed the output buffer size */
66ee282a 3449 strncat(buf, currlogfilename, 128);
e1c8e0ed 3450 buf[strlen(buf)] = '\0';
3451 logevent(buf);
3452
66ee282a 3453 /* --- write header line into log file */
32874aea 3454 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
66ee282a 3455 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
32874aea 3456 fputs(buf, lgfp);
3457 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3458 }
3459}
3460
32874aea 3461void logfclose(void)
3462{
3463 if (lgfp) {
3464 fclose(lgfp);
3465 lgfp = NULL;
3466 }
e1c8e0ed 3467}
66ee282a 3468
3469/*
3470 * translate format codes into time/date strings
3471 * and insert them into log file name
3472 *
3473 * "&Y":YYYY "&m":MM "&d":DD "&T":hhmm "&h":<hostname> "&&":&
3474 */
3475static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
3476 char buf[10], *bufp;
3477 int size;
3478 char *ds = d; /* save start pos. */
3479 int len = FILENAME_MAX-1;
3480
3481 while (*s) {
3482 /* Let (bufp, len) be the string to append. */
3483 bufp = buf; /* don't usually override this */
3484 if (*s == '&') {
3485 char c;
3486 s++;
3487 if (*s) switch (c = *s++, tolower(c)) {
3488 case 'y':
3489 size = strftime(buf, sizeof(buf), "%Y", tm);
3490 break;
3491 case 'm':
3492 size = strftime(buf, sizeof(buf), "%m", tm);
3493 break;
3494 case 'd':
3495 size = strftime(buf, sizeof(buf), "%d", tm);
3496 break;
3497 case 't':
3498 size = strftime(buf, sizeof(buf), "%H%M%S", tm);
3499 break;
3500 case 'h':
3501 bufp = hostname;
3502 size = strlen(bufp);
3503 break;
3504 default:
3505 buf[0] = '&';
3506 size = 1;
3507 if (c != '&')
3508 buf[size++] = c;
3509 }
3510 } else {
3511 buf[0] = *s++;
3512 size = 1;
3513 }
3514 if (size > len)
3515 size = len;
3516 memcpy(d, bufp, size);
3517 d += size;
3518 len -= size;
3519 }
3520 *d = '\0';
3521}