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