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