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