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