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