Terminal fix from RDB: probably didn't mean to compare a Y value
[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);
1542 case 'B':
1543 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1544 seen_disp_event = TRUE;
1545 break;
1546 case 'a': /* move right N cols */
1547 compatibility(ANSI);
1548 case ANSI('c', '>'): /* report xterm version */
1549 compatibility(OTHER);
1550 /* this reports xterm version 136 so that VIM can
1551 use the drag messages from the mouse reporting */
1552 ldisc_send("\033[>0;136;0c", 11);
1553 break;
1554 case 'C':
1555 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1556 seen_disp_event = TRUE;
1557 break;
1558 case 'D': /* move left N cols */
1559 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1560 seen_disp_event = TRUE;
1561 break;
1562 case 'E': /* move down N lines and CR */
1563 compatibility(ANSI);
1564 move(0, curs.y + def(esc_args[0], 1), 1);
1565 seen_disp_event = TRUE;
1566 break;
1567 case 'F': /* move up N lines and CR */
1568 compatibility(ANSI);
1569 move(0, curs.y - def(esc_args[0], 1), 1);
1570 seen_disp_event = TRUE;
1571 break;
1572 case 'G':
1573 case '`': /* set horizontal posn */
1574 compatibility(ANSI);
1575 move(def(esc_args[0], 1) - 1, curs.y, 0);
1576 seen_disp_event = TRUE;
1577 break;
1578 case 'd': /* set vertical posn */
1579 compatibility(ANSI);
1580 move(curs.x,
1581 (dec_om ? marg_t : 0) + def(esc_args[0],
1582 1) - 1,
1583 (dec_om ? 2 : 0));
1584 seen_disp_event = TRUE;
1585 break;
1586 case 'H':
1587 case 'f': /* set horz and vert posns at once */
1588 if (esc_nargs < 2)
1589 esc_args[1] = ARG_DEFAULT;
1590 move(def(esc_args[1], 1) - 1,
1591 (dec_om ? marg_t : 0) + def(esc_args[0],
1592 1) - 1,
1593 (dec_om ? 2 : 0));
1594 seen_disp_event = TRUE;
1595 break;
1596 case 'J': /* erase screen or parts of it */
1597 {
1598 unsigned int i = def(esc_args[0], 0) + 1;
1599 if (i > 3)
1600 i = 0;
1601 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1602 }
1603 disptop = 0;
1604 seen_disp_event = TRUE;
1605 break;
1606 case 'K': /* erase line or parts of it */
1607 {
1608 unsigned int i = def(esc_args[0], 0) + 1;
1609 if (i > 3)
1610 i = 0;
1611 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1612 }
1613 seen_disp_event = TRUE;
1614 break;
1615 case 'L': /* insert lines */
1616 compatibility(VT102);
1617 if (curs.y <= marg_b)
1618 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1619 FALSE);
1620 fix_cpos;
1621 seen_disp_event = TRUE;
1622 break;
1623 case 'M': /* delete lines */
1624 compatibility(VT102);
1625 if (curs.y <= marg_b)
1626 scroll(curs.y, marg_b, def(esc_args[0], 1),
1627 TRUE);
1628 fix_cpos;
1629 seen_disp_event = TRUE;
1630 break;
1631 case '@': /* insert chars */
1632 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1633 compatibility(VT102);
1634 insch(def(esc_args[0], 1));
1635 seen_disp_event = TRUE;
1636 break;
1637 case 'P': /* delete chars */
1638 compatibility(VT102);
1639 insch(-def(esc_args[0], 1));
1640 seen_disp_event = TRUE;
1641 break;
1642 case 'c': /* terminal type query */
1643 compatibility(VT100);
1644 /* This is the response for a VT102 */
1645 ldisc_send(id_string, strlen(id_string));
1646 break;
1647 case 'n': /* cursor position query */
1648 if (esc_args[0] == 6) {
1649 char buf[32];
1650 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1651 curs.x + 1);
1652 ldisc_send(buf, strlen(buf));
1653 } else if (esc_args[0] == 5) {
1654 ldisc_send("\033[0n", 4);
1655 }
1656 break;
1657 case 'h': /* toggle modes to high */
1658 case ANSI_QUE('h'):
1659 compatibility(VT100);
1660 {
1661 int i;
1662 for (i = 0; i < esc_nargs; i++)
1663 toggle_mode(esc_args[i], esc_query, TRUE);
1664 }
1665 break;
1666 case 'l': /* toggle modes to low */
1667 case ANSI_QUE('l'):
1668 compatibility(VT100);
1669 {
1670 int i;
1671 for (i = 0; i < esc_nargs; i++)
1672 toggle_mode(esc_args[i], esc_query, FALSE);
1673 }
1674 break;
1675 case 'g': /* clear tabs */
1676 compatibility(VT100);
1677 if (esc_nargs == 1) {
1678 if (esc_args[0] == 0) {
1679 tabs[curs.x] = FALSE;
1680 } else if (esc_args[0] == 3) {
1681 int i;
1682 for (i = 0; i < cols; i++)
1683 tabs[i] = FALSE;
1684 }
1685 }
1686 break;
1687 case 'r': /* set scroll margins */
1688 compatibility(VT100);
1689 if (esc_nargs <= 2) {
1690 int top, bot;
1691 top = def(esc_args[0], 1) - 1;
1692 bot = (esc_nargs <= 1
1693 || esc_args[1] ==
1694 0 ? rows : def(esc_args[1], rows)) - 1;
1695 if (bot >= rows)
1696 bot = rows - 1;
1697 /* VTTEST Bug 9 - if region is less than 2 lines
1698 * don't change region.
1699 */
1700 if (bot - top > 0) {
1701 marg_t = top;
1702 marg_b = bot;
1703 curs.x = 0;
1704 /*
1705 * I used to think the cursor should be
1706 * placed at the top of the newly marginned
1707 * area. Apparently not: VMS TPU falls over
1708 * if so.
1709 *
1710 * Well actually it should for Origin mode - RDB
1711 */
1712 curs.y = (dec_om ? marg_t : 0);
1713 fix_cpos;
1714 seen_disp_event = TRUE;
1715 }
1716 }
1717 break;
1718 case 'm': /* set graphics rendition */
1719 {
1720 /*
1721 * A VT100 without the AVO only had one attribute, either
1722 * underline or reverse video depending on the cursor type,
1723 * this was selected by CSI 7m.
1724 *
1725 * case 2:
4eeb7d09 1726 * This is sometimes DIM, eg on the GIGI and Linux
32874aea 1727 * case 8:
4eeb7d09 1728 * This is sometimes INVIS various ANSI.
32874aea 1729 * case 21:
1730 * This like 22 disables BOLD, DIM and INVIS
1731 *
1732 * The ANSI colours appear on any terminal that has colour
1733 * (obviously) but the interaction between sgr0 and the
1734 * colours varies but is usually related to the background
1735 * colour erase item.
1736 * The interaction between colour attributes and the mono
1737 * ones is also very implementation dependent.
1738 *
1739 * The 39 and 49 attributes are likely to be unimplemented.
1740 */
1741 int i;
1742 for (i = 0; i < esc_nargs; i++) {
1743 switch (def(esc_args[i], 0)) {
1744 case 0: /* restore defaults */
1745 curr_attr = ATTR_DEFAULT;
1746 break;
1747 case 1: /* enable bold */
1748 compatibility(VT100AVO);
1749 curr_attr |= ATTR_BOLD;
1750 break;
1751 case 21: /* (enable double underline) */
1752 compatibility(OTHER);
1753 case 4: /* enable underline */
1754 compatibility(VT100AVO);
1755 curr_attr |= ATTR_UNDER;
1756 break;
1757 case 5: /* enable blink */
1758 compatibility(VT100AVO);
1759 curr_attr |= ATTR_BLINK;
1760 break;
1761 case 7: /* enable reverse video */
1762 curr_attr |= ATTR_REVERSE;
1763 break;
1764 case 22: /* disable bold */
1765 compatibility2(OTHER, VT220);
1766 curr_attr &= ~ATTR_BOLD;
1767 break;
1768 case 24: /* disable underline */
1769 compatibility2(OTHER, VT220);
1770 curr_attr &= ~ATTR_UNDER;
1771 break;
1772 case 25: /* disable blink */
1773 compatibility2(OTHER, VT220);
1774 curr_attr &= ~ATTR_BLINK;
1775 break;
1776 case 27: /* disable reverse video */
1777 compatibility2(OTHER, VT220);
1778 curr_attr &= ~ATTR_REVERSE;
1779 break;
1780 case 30:
1781 case 31:
1782 case 32:
1783 case 33:
1784 case 34:
1785 case 35:
1786 case 36:
1787 case 37:
1788 /* foreground */
1789 curr_attr &= ~ATTR_FGMASK;
1790 curr_attr |=
1791 (esc_args[i] - 30) << ATTR_FGSHIFT;
1792 break;
1793 case 39: /* default-foreground */
1794 curr_attr &= ~ATTR_FGMASK;
1795 curr_attr |= ATTR_DEFFG;
1796 break;
1797 case 40:
1798 case 41:
1799 case 42:
1800 case 43:
1801 case 44:
1802 case 45:
1803 case 46:
1804 case 47:
1805 /* background */
1806 curr_attr &= ~ATTR_BGMASK;
1807 curr_attr |=
1808 (esc_args[i] - 40) << ATTR_BGSHIFT;
1809 break;
1810 case 49: /* default-background */
1811 curr_attr &= ~ATTR_BGMASK;
1812 curr_attr |= ATTR_DEFBG;
1813 break;
1814 }
1815 }
1816 if (use_bce)
1817 erase_char =
1818 (' ' |
1819 (curr_attr &
1820 (ATTR_FGMASK | ATTR_BGMASK |
1821 ATTR_BLINK)));
1822 }
1823 break;
1824 case 's': /* save cursor */
1825 save_cursor(TRUE);
1826 break;
1827 case 'u': /* restore cursor */
1828 save_cursor(FALSE);
1829 seen_disp_event = TRUE;
1830 break;
1831 case 't': /* set page size - ie window height */
374330e2 1832 /*
32874aea 1833 * VT340/VT420 sequence DECSLPP, DEC only allows values
1834 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1835 * illegal values (eg first arg 1..9) for window changing
1836 * and reports.
1837 */
1838 compatibility(VT340TEXT);
1839 if (esc_nargs <= 1
1840 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1841 request_resize(cols, def(esc_args[0], 24), 0);
1842 deselect();
1843 }
1844 break;
1a837633 1845 case 'S':
1846 compatibility(SCOANSI);
1847 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1848 fix_cpos;
1849 wrapnext = FALSE;
1850 seen_disp_event = TRUE;
1851 break;
1852 case 'T':
1853 compatibility(SCOANSI);
1854 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1855 fix_cpos;
1856 wrapnext = FALSE;
1857 seen_disp_event = TRUE;
1858 break;
32874aea 1859 case ANSI('|', '*'):
1860 /* VT420 sequence DECSNLS
1861 * Set number of lines on screen
1862 * VT420 uses VGA like hardware and can support any size in
1863 * reasonable range (24..49 AIUI) with no default specified.
1864 */
1865 compatibility(VT420);
1866 if (esc_nargs == 1 && esc_args[0] > 0) {
1867 request_resize(cols,
1868 def(esc_args[0], cfg.height),
1869 0);
1870 deselect();
1871 }
1872 break;
1873 case ANSI('|', '$'):
1874 /* VT340/VT420 sequence DECSCPP
1875 * Set number of columns per page
1876 * Docs imply range is only 80 or 132, but I'll allow any.
1877 */
1878 compatibility(VT340TEXT);
1879 if (esc_nargs <= 1) {
1880 request_resize(def(esc_args[0], cfg.width),
1881 rows, 0);
1882 deselect();
1883 }
1884 break;
1885 case 'X': /* write N spaces w/o moving cursor */
1886 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1887 compatibility(ANSIMIN);
1888 {
1889 int n = def(esc_args[0], 1);
1890 pos cursplus;
1891 unsigned long *p = cpos;
1892 if (n > cols - curs.x)
1893 n = cols - curs.x;
1894 cursplus = curs;
1895 cursplus.x += n;
1896 check_selection(curs, cursplus);
1897 while (n--)
1898 *p++ = erase_char;
1899 seen_disp_event = TRUE;
1900 }
1901 break;
1902 case 'x': /* report terminal characteristics */
1903 compatibility(VT100);
1904 {
1905 char buf[32];
1906 int i = def(esc_args[0], 0);
1907 if (i == 0 || i == 1) {
1908 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1909 buf[2] += i;
1910 ldisc_send(buf, 20);
1911 }
1912 }
1913 break;
1914 case ANSI('L', '='):
1915 compatibility(OTHER);
1916 use_bce = (esc_args[0] <= 0);
1917 erase_char = ERASE_CHAR;
1918 if (use_bce)
1919 erase_char =
1920 (' ' |
1921 (curr_attr &
1922 (ATTR_FGMASK | ATTR_BGMASK)));
1923 break;
1924 case ANSI('E', '='):
1925 compatibility(OTHER);
1926 blink_is_real = (esc_args[0] >= 1);
1927 break;
1928 case ANSI('p', '"'):
1929 /* Allow the host to make this emulator a 'perfect' VT102.
1930 * This first appeared in the VT220, but we do need to get
1931 * back to PuTTY mode so I won't check it.
e14a5a13 1932 *
4eeb7d09 1933 * The arg in 40..42,50 are a PuTTY extension.
32874aea 1934 * The 2nd arg, 8bit vs 7bit is not checked.
1935 *
1936 * Setting VT102 mode should also change the Fkeys to
1937 * generate PF* codes as a real VT102 has no Fkeys.
1938 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1939 * send nothing.
1940 *
1941 * Note ESC c will NOT change this!
374330e2 1942 */
32874aea 1943
1944 switch (esc_args[0]) {
1945 case 61:
1946 compatibility_level &= ~TM_VTXXX;
1947 compatibility_level |= TM_VT102;
374330e2 1948 break;
32874aea 1949 case 62:
1950 compatibility_level &= ~TM_VTXXX;
1951 compatibility_level |= TM_VT220;
1952 break;
1953
1954 default:
1955 if (esc_args[0] > 60 && esc_args[0] < 70)
1956 compatibility_level |= TM_VTXXX;
1957 break;
1958
1959 case 40:
1960 compatibility_level &= TM_VTXXX;
374330e2 1961 break;
32874aea 1962 case 41:
1963 compatibility_level = TM_PUTTY;
374330e2 1964 break;
32874aea 1965 case 42:
1966 compatibility_level = TM_SCOANSI;
374330e2 1967 break;
32874aea 1968
1969 case ARG_DEFAULT:
1970 compatibility_level = TM_PUTTY;
1971 break;
1972 case 50:
1973 break;
1974 }
1975
1976 /* Change the response to CSI c */
1977 if (esc_args[0] == 50) {
1978 int i;
1979 char lbuf[64];
1980 strcpy(id_string, "\033[?");
1981 for (i = 1; i < esc_nargs; i++) {
1982 if (i != 1)
1983 strcat(id_string, ";");
1984 sprintf(lbuf, "%d", esc_args[i]);
1985 strcat(id_string, lbuf);
1986 }
1987 strcat(id_string, "c");
1988 }
1989#if 0
1990 /* Is this a good idea ?
1991 * Well we should do a soft reset at this point ...
1992 */
1993 if (!has_compat(VT420) && has_compat(VT100)) {
1994 if (reset_132)
1995 request_resize(132, 24, 1);
1996 else
1997 request_resize(80, 24, 1);
374330e2 1998 }
32874aea 1999#endif
2000 break;
374330e2 2001 }
374330e2 2002 break;
32874aea 2003 case SEEN_OSC:
2004 osc_w = FALSE;
2005 switch (c) {
2006 case 'P': /* Linux palette sequence */
2007 termstate = SEEN_OSC_P;
2008 osc_strlen = 0;
2009 break;
2010 case 'R': /* Linux palette reset */
2011 palette_reset();
2012 term_invalidate();
2013 termstate = TOPLEVEL;
2014 break;
2015 case 'W': /* word-set */
2016 termstate = SEEN_OSC_W;
2017 osc_w = TRUE;
2018 break;
2019 case '0':
2020 case '1':
2021 case '2':
2022 case '3':
2023 case '4':
2024 case '5':
2025 case '6':
2026 case '7':
2027 case '8':
2028 case '9':
2029 esc_args[0] = 10 * esc_args[0] + c - '0';
2030 break;
2031 case 'L':
2032 /*
2033 * Grotty hack to support xterm and DECterm title
2034 * sequences concurrently.
2035 */
2036 if (esc_args[0] == 2) {
2037 esc_args[0] = 1;
2038 break;
2039 }
2040 /* else fall through */
2041 default:
2042 termstate = OSC_STRING;
2043 osc_strlen = 0;
e14a5a13 2044 }
2045 break;
32874aea 2046 case OSC_STRING:
2047 /*
2048 * This OSC stuff is EVIL. It takes just one character to get into
2049 * sysline mode and it's not initially obvious how to get out.
2050 * So I've added CR and LF as string aborts.
2051 * This shouldn't effect compatibility as I believe embedded
2052 * control characters are supposed to be interpreted (maybe?)
2053 * and they don't display anything useful anyway.
2054 *
2055 * -- RDB
e14a5a13 2056 */
32874aea 2057 if (c == '\n' || c == '\r') {
2058 termstate = TOPLEVEL;
2059 } else if (c == 0234 || c == '\007') {
2060 /*
2061 * These characters terminate the string; ST and BEL
2062 * terminate the sequence and trigger instant
2063 * processing of it, whereas ESC goes back to SEEN_ESC
2064 * mode unless it is followed by \, in which case it is
2065 * synonymous with ST in the first place.
2066 */
2067 do_osc();
2068 termstate = TOPLEVEL;
2069 } else if (c == '\033')
2070 termstate = OSC_MAYBE_ST;
2071 else if (osc_strlen < OSC_STR_MAX)
2072 osc_string[osc_strlen++] = c;
374330e2 2073 break;
32874aea 2074 case SEEN_OSC_P:
374330e2 2075 {
32874aea 2076 int max = (osc_strlen == 0 ? 21 : 16);
2077 int val;
2078 if (c >= '0' && c <= '9')
2079 val = c - '0';
2080 else if (c >= 'A' && c <= 'A' + max - 10)
2081 val = c - 'A' + 10;
2082 else if (c >= 'a' && c <= 'a' + max - 10)
2083 val = c - 'a' + 10;
2d466ffd 2084 else {
32874aea 2085 termstate = TOPLEVEL;
2d466ffd 2086 break;
2087 }
32874aea 2088 osc_string[osc_strlen++] = val;
2089 if (osc_strlen >= 7) {
2090 palette_set(osc_string[0],
2091 osc_string[1] * 16 + osc_string[2],
2092 osc_string[3] * 16 + osc_string[4],
2093 osc_string[5] * 16 + osc_string[6]);
2094 term_invalidate();
2095 termstate = TOPLEVEL;
374330e2 2096 }
2097 }
2098 break;
32874aea 2099 case SEEN_OSC_W:
2100 switch (c) {
2101 case '0':
2102 case '1':
2103 case '2':
2104 case '3':
2105 case '4':
2106 case '5':
2107 case '6':
2108 case '7':
2109 case '8':
2110 case '9':
2111 esc_args[0] = 10 * esc_args[0] + c - '0';
2112 break;
2113 default:
2114 termstate = OSC_STRING;
2115 osc_strlen = 0;
ec55b220 2116 }
32874aea 2117 break;
32874aea 2118 case VT52_ESC:
374330e2 2119 termstate = TOPLEVEL;
32874aea 2120 seen_disp_event = TRUE;
c9def1b8 2121 switch (c) {
32874aea 2122 case 'A':
2123 move(curs.x, curs.y - 1, 1);
2124 break;
2125 case 'B':
2126 move(curs.x, curs.y + 1, 1);
2127 break;
2128 case 'C':
2129 move(curs.x + 1, curs.y, 1);
2130 break;
2131 case 'D':
2132 move(curs.x - 1, curs.y, 1);
2133 break;
4eeb7d09 2134 /*
2135 * From the VT100 Manual
2136 * NOTE: The special graphics characters in the VT100
2137 * are different from those in the VT52
2138 *
2139 * From VT102 manual:
2140 * 137 _ Blank - Same
2141 * 140 ` Reserved - Humm.
2142 * 141 a Solid rectangle - Similar
2143 * 142 b 1/ - Top half of fraction for the
2144 * 143 c 3/ - subscript numbers below.
2145 * 144 d 5/
2146 * 145 e 7/
2147 * 146 f Degrees - Same
2148 * 147 g Plus or minus - Same
2149 * 150 h Right arrow
2150 * 151 i Ellipsis (dots)
2151 * 152 j Divide by
2152 * 153 k Down arrow
2153 * 154 l Bar at scan 0
2154 * 155 m Bar at scan 1
2155 * 156 n Bar at scan 2
2156 * 157 o Bar at scan 3 - Similar
2157 * 160 p Bar at scan 4 - Similar
2158 * 161 q Bar at scan 5 - Similar
2159 * 162 r Bar at scan 6 - Same
2160 * 163 s Bar at scan 7 - Similar
2161 * 164 t Subscript 0
2162 * 165 u Subscript 1
2163 * 166 v Subscript 2
2164 * 167 w Subscript 3
2165 * 170 x Subscript 4
2166 * 171 y Subscript 5
2167 * 172 z Subscript 6
2168 * 173 { Subscript 7
2169 * 174 | Subscript 8
2170 * 175 } Subscript 9
2171 * 176 ~ Paragraph
2172 *
2173 */
32874aea 2174 case 'F':
2175 cset_attr[cset = 0] = ATTR_LINEDRW;
2176 break;
2177 case 'G':
2178 cset_attr[cset = 0] = ATTR_ASCII;
2179 break;
2180 case 'H':
2181 move(0, 0, 0);
2182 break;
2183 case 'I':
2184 if (curs.y == 0)
2185 scroll(0, rows - 1, -1, TRUE);
2186 else if (curs.y > 0)
2187 curs.y--;
2188 fix_cpos;
2189 wrapnext = FALSE;
2190 break;
2191 case 'J':
2192 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2193 disptop = 0;
c9def1b8 2194 break;
32874aea 2195 case 'K':
2196 erase_lots(TRUE, FALSE, TRUE);
2197 break;
4eeb7d09 2198#if 0
32874aea 2199 case 'V':
2200 /* XXX Print cursor line */
2201 break;
2202 case 'W':
2203 /* XXX Start controller mode */
2204 break;
2205 case 'X':
2206 /* XXX Stop controller mode */
2207 break;
4eeb7d09 2208#endif
32874aea 2209 case 'Y':
2210 termstate = VT52_Y1;
2211 break;
2212 case 'Z':
2213 ldisc_send("\033/Z", 3);
2214 break;
2215 case '=':
2216 app_keypad_keys = TRUE;
2217 break;
2218 case '>':
2219 app_keypad_keys = FALSE;
2220 break;
2221 case '<':
2222 /* XXX This should switch to VT100 mode not current or default
2223 * VT mode. But this will only have effect in a VT220+
2224 * emulation.
2225 */
2226 vt52_mode = FALSE;
4eeb7d09 2227 blink_is_real = cfg.blinktext;
32874aea 2228 break;
4eeb7d09 2229#if 0
32874aea 2230 case '^':
2231 /* XXX Enter auto print mode */
2232 break;
2233 case '_':
2234 /* XXX Exit auto print mode */
2235 break;
2236 case ']':
2237 /* XXX Print screen */
2238 break;
4eeb7d09 2239#endif
2240
2241#ifdef VT52_PLUS
2242 case 'E':
2243 /* compatibility(ATARI) */
2244 move(0, 0, 0);
2245 erase_lots(FALSE, FALSE, TRUE);
2246 disptop = 0;
2247 break;
2248 case 'L':
2249 /* compatibility(ATARI) */
2250 if (curs.y <= marg_b)
2251 scroll(curs.y, marg_b, -1, FALSE);
2252 break;
2253 case 'M':
2254 /* compatibility(ATARI) */
2255 if (curs.y <= marg_b)
2256 scroll(curs.y, marg_b, 1, TRUE);
2257 break;
2258 case 'b':
2259 /* compatibility(ATARI) */
2260 termstate = VT52_FG;
2261 break;
2262 case 'c':
2263 /* compatibility(ATARI) */
2264 termstate = VT52_BG;
2265 break;
2266 case 'd':
2267 /* compatibility(ATARI) */
2268 erase_lots(FALSE, TRUE, FALSE);
2269 disptop = 0;
2270 break;
2271 case 'e':
2272 /* compatibility(ATARI) */
2273 cursor_on = TRUE;
2274 break;
2275 case 'f':
2276 /* compatibility(ATARI) */
2277 cursor_on = FALSE;
2278 break;
2279 /* case 'j': Save cursor position - broken on ST */
2280 /* case 'k': Restore cursor position */
2281 case 'l':
2282 /* compatibility(ATARI) */
2283 erase_lots(TRUE, TRUE, TRUE);
2284 curs.x = 0;
2285 wrapnext = FALSE;
2286 fix_cpos;
2287 break;
2288 case 'o':
2289 /* compatibility(ATARI) */
2290 erase_lots(TRUE, TRUE, FALSE);
2291 break;
2292 case 'p':
2293 /* compatibility(ATARI) */
2294 curr_attr |= ATTR_REVERSE;
2295 break;
2296 case 'q':
2297 /* compatibility(ATARI) */
2298 curr_attr &= ~ATTR_REVERSE;
2299 break;
2300 case 'v': /* wrap Autowrap on - Wyse style */
2301 /* compatibility(ATARI) */
2302 wrap = 1;
2303 break;
2304 case 'w': /* Autowrap off */
2305 /* compatibility(ATARI) */
2306 wrap = 0;
2307 break;
2308
2309 case 'R':
2310 /* compatibility(OTHER) */
2311 vt52_bold = FALSE;
2312 curr_attr = ATTR_DEFAULT;
2313 if (use_bce)
2314 erase_char = (' ' |
2315 (curr_attr &
2316 (ATTR_FGMASK | ATTR_BGMASK |
2317 ATTR_BLINK)));
2318 break;
2319 case 'S':
2320 /* compatibility(VI50) */
2321 curr_attr |= ATTR_UNDER;
2322 break;
2323 case 'W':
2324 /* compatibility(VI50) */
2325 curr_attr &= ~ATTR_UNDER;
2326 break;
2327 case 'U':
2328 /* compatibility(VI50) */
2329 vt52_bold = TRUE;
2330 curr_attr |= ATTR_BOLD;
2331 break;
2332 case 'T':
2333 /* compatibility(VI50) */
2334 vt52_bold = FALSE;
2335 curr_attr &= ~ATTR_BOLD;
2336 break;
2337#endif
c9def1b8 2338 }
e14a5a13 2339 break;
32874aea 2340 case VT52_Y1:
2341 termstate = VT52_Y2;
2342 move(curs.x, c - ' ', 0);
e14a5a13 2343 break;
32874aea 2344 case VT52_Y2:
2345 termstate = TOPLEVEL;
2346 move(c - ' ', curs.y, 0);
e14a5a13 2347 break;
4eeb7d09 2348
2349#ifdef VT52_PLUS
2350 case VT52_FG:
2351 termstate = TOPLEVEL;
2352 curr_attr &= ~ATTR_FGMASK;
2353 curr_attr &= ~ATTR_BOLD;
2354 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2355 if ((c & 0x8) || vt52_bold)
2356 curr_attr |= ATTR_BOLD;
2357
2358 if (use_bce)
2359 erase_char = (' ' |
2360 (curr_attr &
2361 (ATTR_FGMASK | ATTR_BGMASK |
2362 ATTR_BLINK)));
2363 break;
2364 case VT52_BG:
2365 termstate = TOPLEVEL;
2366 curr_attr &= ~ATTR_BGMASK;
2367 curr_attr &= ~ATTR_BLINK;
2368 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2369
2370 /* Note: bold background */
2371 if (c & 0x8)
2372 curr_attr |= ATTR_BLINK;
2373
2374 if (use_bce)
2375 erase_char = (' ' |
2376 (curr_attr &
2377 (ATTR_FGMASK | ATTR_BGMASK |
2378 ATTR_BLINK)));
2379 break;
2380#endif
2d466ffd 2381 default: break; /* placate gcc warning about enum use */
e14a5a13 2382 }
4facdf84 2383 if (selstate != NO_SELECTION) {
2384 pos cursplus = curs;
2385 incpos(cursplus);
32874aea 2386 check_selection(curs, cursplus);
4facdf84 2387 }
374330e2 2388 }
c9def1b8 2389 inbuf_head = 0;
374330e2 2390}
2391
4eeb7d09 2392#if 0
374330e2 2393/*
2394 * Compare two lines to determine whether they are sufficiently
2395 * alike to scroll-optimise one to the other. Return the degree of
2396 * similarity.
2397 */
32874aea 2398static int linecmp(unsigned long *a, unsigned long *b)
2399{
374330e2 2400 int i, n;
2401
32874aea 2402 for (i = n = 0; i < cols; i++)
374330e2 2403 n += (*a++ == *b++);
2404 return n;
2405}
4eeb7d09 2406#endif
374330e2 2407
2408/*
2409 * Given a context, update the window. Out of paranoia, we don't
2410 * allow WM_PAINT responses to do scrolling optimisations.
2411 */
32874aea 2412static void do_paint(Context ctx, int may_optimise)
2413{
4eeb7d09 2414 int i, j, our_curs_y;
2415 unsigned long rv, cursor;
4facdf84 2416 pos scrpos;
374330e2 2417 char ch[1024];
4eeb7d09 2418 long cursor_background = ERASE_CHAR;
156686ef 2419 long ticks;
374330e2 2420
156686ef 2421 /*
2422 * Check the visual bell state.
2423 */
2424 if (in_vbell) {
2425 ticks = GetTickCount();
2426 if (ticks - vbell_timeout >= 0)
2427 in_vbell = FALSE;
2428 }
2429
4eeb7d09 2430 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2431
156686ef 2432 /* Depends on:
2433 * screen array, disptop, scrtop,
2434 * selection, rv,
2435 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2436 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2437 */
4eeb7d09 2438
2439 /* Has the cursor position or type changed ? */
e14a5a13 2440 if (cursor_on) {
32874aea 2441 if (has_focus) {
217dceef 2442 if (blinker || !cfg.blink_cur)
4eeb7d09 2443 cursor = TATTR_ACTCURS;
32874aea 2444 else
2445 cursor = 0;
2446 } else
4eeb7d09 2447 cursor = TATTR_PASCURS;
4e30ff69 2448 if (wrapnext)
4eeb7d09 2449 cursor |= TATTR_RIGHTCURS;
32874aea 2450 } else
156686ef 2451 cursor = 0;
4facdf84 2452 our_curs_y = curs.y - disptop;
374330e2 2453
4eeb7d09 2454 if (dispcurs && (curstype != cursor ||
2455 dispcurs !=
2456 disptext + our_curs_y * (cols + 1) + curs.x)) {
2457 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2458 dispcurs[-1] |= ATTR_INVALID;
2459 if ((*dispcurs & ATTR_WIDE))
2460 dispcurs[1] |= ATTR_INVALID;
2461 *dispcurs |= ATTR_INVALID;
2462 curstype = 0;
2463 }
2464 dispcurs = NULL;
2465
2466 /* The normal screen data */
32874aea 2467 for (i = 0; i < rows; i++) {
4facdf84 2468 unsigned long *ldata;
2469 int lattr;
4eeb7d09 2470 int idx, dirty_line, dirty_run;
2471 unsigned long attr = 0;
2472 int updated_line = 0;
2473 int start = 0;
2474 int ccount = 0;
2475 int last_run_dirty = 0;
2476
4facdf84 2477 scrpos.y = i + disptop;
2478 ldata = lineptr(scrpos.y);
2479 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2480
2481 idx = i * (cols + 1);
2482 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2483 disptext[idx + cols] = ldata[cols];
2484
2485 for (j = 0; j < cols; j++, idx++) {
2486 unsigned long tattr, tchar;
2487 unsigned long *d = ldata + j;
2488 int break_run;
4facdf84 2489 scrpos.x = j;
32874aea 2490
4eeb7d09 2491 tchar = (*d & (CHAR_MASK | CSET_MASK));
2492 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2493 switch (tchar & CSET_MASK) {
2494 case ATTR_ASCII:
2495 tchar = unitab_line[tchar & 0xFF];
2496 break;
2497 case ATTR_LINEDRW:
2498 tchar = unitab_xterm[tchar & 0xFF];
2499 break;
2500 }
2501 tattr |= (tchar & CSET_MASK);
2502 tchar &= CHAR_MASK;
2503
2504 /* Video reversing things */
2505 tattr = (tattr ^ rv
2506 ^ (posle(selstart, scrpos) &&
2507 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2508
2509 /* 'Real' blinking ? */
2510 if (blink_is_real && (tattr & ATTR_BLINK)) {
2511 if (has_focus && tblinker) {
2512 tchar = ' ';
2513 tattr &= ~CSET_MASK;
2514 tattr |= ATTR_ACP;
c9def1b8 2515 }
4eeb7d09 2516 tattr &= ~ATTR_BLINK;
c9def1b8 2517 }
374330e2 2518
4eeb7d09 2519 /* Cursor here ? Save the 'background' */
2520 if (i == our_curs_y && j == curs.x) {
2521 cursor_background = tattr | tchar;
2522 dispcurs = disptext + idx;
2523 }
374330e2 2524
4eeb7d09 2525 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2526 dirty_line = TRUE;
2527
2528 break_run = (tattr != attr || j - start >= sizeof(ch));
2529
2530 /* Special hack for VT100 Linedraw glyphs */
2531 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2532 && tchar <= 0xBD) break_run = TRUE;
2533
2534 if (!dbcs_screenfont && !dirty_line) {
2535 if ((tchar | tattr) == disptext[idx])
2536 break_run = TRUE;
2537 else if (!dirty_run && ccount == 1)
2538 break_run = TRUE;
374330e2 2539 }
4eeb7d09 2540
2541 if (break_run) {
2542 if ((dirty_run || last_run_dirty) && ccount > 0) {
2543 do_text(ctx, start, i, ch, ccount, attr, lattr);
2544 updated_line = 1;
2545 }
2546 start = j;
2547 ccount = 0;
2548 attr = tattr;
2549 if (dbcs_screenfont)
2550 last_run_dirty = dirty_run;
2551 dirty_run = dirty_line;
2552 }
2553
2554 if ((tchar | tattr) != disptext[idx])
2555 dirty_run = TRUE;
2556 ch[ccount++] = (char) tchar;
2557 disptext[idx] = tchar | tattr;
2558
2559 /* If it's a wide char step along to the next one. */
2560 if (tattr & ATTR_WIDE) {
2561 if (++j < cols) {
2562 idx++;
2563 d++;
2564 /* Cursor is here ? Ouch! */
2565 if (i == our_curs_y && j == curs.x) {
2566 cursor_background = *d;
2567 dispcurs = disptext + idx;
2568 }
2569 if (disptext[idx] != *d)
2570 dirty_run = TRUE;
2571 disptext[idx] = *d;
374330e2 2572 }
374330e2 2573 }
4eeb7d09 2574 }
2575 if (dirty_run && ccount > 0) {
2576 do_text(ctx, start, i, ch, ccount, attr, lattr);
2577 updated_line = 1;
2578 }
2579
2580 /* Cursor on this line ? (and changed) */
2581 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2582 ch[0] = (char) (cursor_background & CHAR_MASK);
2583 attr = (cursor_background & ATTR_MASK) | cursor;
2584 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2585 curstype = cursor;
374330e2 2586 }
2587 }
2588}
2589
2590/*
e14a5a13 2591 * Flick the switch that says if blinking things should be shown or hidden.
2592 */
2593
32874aea 2594void term_blink(int flg)
2595{
22dcdc3b 2596 static long last_blink = 0;
2597 static long last_tblink = 0;
e14a5a13 2598 long now, blink_diff;
2599
c9def1b8 2600 now = GetTickCount();
32874aea 2601 blink_diff = now - last_tblink;
c9def1b8 2602
2603 /* Make sure the text blinks no more than 2Hz */
32874aea 2604 if (blink_diff < 0 || blink_diff > 450) {
2605 last_tblink = now;
c9def1b8 2606 tblinker = !tblinker;
2607 }
2608
e14a5a13 2609 if (flg) {
32874aea 2610 blinker = 1;
2611 last_blink = now;
e14a5a13 2612 return;
32874aea 2613 }
e14a5a13 2614
32874aea 2615 blink_diff = now - last_blink;
e14a5a13 2616
22dcdc3b 2617 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2618 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2619 return;
2620
e14a5a13 2621 last_blink = now;
2622 blinker = !blinker;
2623}
2624
2625/*
374330e2 2626 * Invalidate the whole screen so it will be repainted in full.
2627 */
32874aea 2628void term_invalidate(void)
2629{
374330e2 2630 int i;
2631
32874aea 2632 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2633 disptext[i] = ATTR_INVALID;
2634}
2635
2636/*
2637 * Paint the window in response to a WM_PAINT message.
2638 */
32874aea 2639void term_paint(Context ctx, int l, int t, int r, int b)
2640{
374330e2 2641 int i, j, left, top, right, bottom;
2642
2643 left = l / font_width;
2644 right = (r - 1) / font_width;
2645 top = t / font_height;
2646 bottom = (b - 1) / font_height;
32874aea 2647 for (i = top; i <= bottom && i < rows; i++) {
2648 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2649 for (j = left; j <= right && j < cols; j++)
2650 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2651 else
32874aea 2652 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2653 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2654 }
374330e2 2655
e14a5a13 2656 /* This should happen soon enough, also for some reason it sometimes
2657 * fails to actually do anything when re-sizing ... painting the wrong
2658 * window perhaps ?
32874aea 2659 do_paint (ctx, FALSE);
2660 */
374330e2 2661}
2662
2663/*
2664 * Attempt to scroll the scrollback. The second parameter gives the
2665 * position we want to scroll to; the first is +1 to denote that
2666 * this position is relative to the beginning of the scrollback, -1
2667 * to denote it is relative to the end, and 0 to denote that it is
2668 * relative to the current position.
2669 */
32874aea 2670void term_scroll(int rel, int where)
2671{
4facdf84 2672 int sbtop = -count234(scrollback);
374330e2 2673
32874aea 2674 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2675 if (disptop < sbtop)
2676 disptop = sbtop;
4facdf84 2677 if (disptop > 0)
2678 disptop = 0;
374330e2 2679 update_sbar();
2680 term_update();
2681}
2682
4eeb7d09 2683static void clipme(pos top, pos bottom)
32874aea 2684{
4eeb7d09 2685 wchar_t *workbuf;
2686 wchar_t *wbptr; /* where next char goes within workbuf */
32874aea 2687 int wblen = 0; /* workbuf len */
2688 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2689
4eeb7d09 2690 buflen = 5120; /* Default size */
2691 workbuf = smalloc(buflen * sizeof(wchar_t));
2692 wbptr = workbuf; /* start filling here */
bc1235d4 2693
4facdf84 2694 while (poslt(top, bottom)) {
bc1235d4 2695 int nl = FALSE;
4facdf84 2696 unsigned long *ldata = lineptr(top.y);
260f3dec 2697 pos nlpos;
4facdf84 2698
2699 nlpos.y = top.y;
2700 nlpos.x = cols;
bc1235d4 2701
4eeb7d09 2702 if (!(ldata[cols] & LATTR_WRAPPED)) {
2703 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2704 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2705 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2706 && poslt(top, nlpos))
2707 decpos(nlpos);
4facdf84 2708 if (poslt(nlpos, bottom))
bc1235d4 2709 nl = TRUE;
2710 }
4facdf84 2711 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2712#if 0
2713 char cbuf[16], *p;
2714 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2715#else
2716 wchar_t cbuf[16], *p;
2717 int uc = (ldata[top.x] & 0xFFFF);
2718 int set, c;
2719
2720 if (uc == UCSWIDE) {
2721 top.x++;
2722 continue;
2723 }
2724
2725 switch (uc & CSET_MASK) {
2726 case ATTR_LINEDRW:
2727 if (!cfg.rawcnp) {
2728 uc = unitab_xterm[uc & 0xFF];
2729 break;
32874aea 2730 }
4eeb7d09 2731 case ATTR_ASCII:
2732 uc = unitab_line[uc & 0xFF];
2733 break;
d3a22f79 2734 }
4eeb7d09 2735 switch (uc & CSET_MASK) {
2736 case ATTR_ACP:
2737 uc = unitab_font[uc & 0xFF];
2738 break;
2739 case ATTR_OEMCP:
2740 uc = unitab_oemcp[uc & 0xFF];
2741 break;
2742 }
2743
2744 set = (uc & CSET_MASK);
2745 c = (uc & CHAR_MASK);
2746 cbuf[0] = uc;
2747 cbuf[1] = 0;
2748
2749 if (DIRECT_FONT(uc)) {
2750 if (c >= ' ' && c != 0x7F) {
2751 unsigned char buf[4];
2752 WCHAR wbuf[4];
2753 int rv;
2754 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2755 buf[0] = c;
2756 buf[1] = (unsigned char) ldata[top.x + 1];
2757 rv = MultiByteToWideChar(font_codepage,
2758 0, buf, 2, wbuf, 4);
2759 top.x++;
2760 } else {
2761 buf[0] = c;
2762 rv = MultiByteToWideChar(font_codepage,
2763 0, buf, 1, wbuf, 4);
2764 }
bc1235d4 2765
4eeb7d09 2766 if (rv > 0) {
2767 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2768 cbuf[rv] = 0;
d3a22f79 2769 }
d3a22f79 2770 }
4eeb7d09 2771 }
2772#endif
2773
2774 for (p = cbuf; *p; p++) {
2775 /* Enough overhead for trailing NL and nul */
2776 if (wblen >= buflen - 16) {
2777 workbuf =
2778 srealloc(workbuf,
2779 sizeof(wchar_t) * (buflen += 100));
2780 wbptr = workbuf + wblen;
2781 }
2782 wblen++;
2783 *wbptr++ = *p;
bc1235d4 2784 }
4facdf84 2785 top.x++;
bc1235d4 2786 }
2787 if (nl) {
2788 int i;
4eeb7d09 2789 for (i = 0; i < sel_nl_sz; i++) {
32874aea 2790 wblen++;
bc1235d4 2791 *wbptr++ = sel_nl[i];
2792 }
2793 }
4facdf84 2794 top.y++;
2795 top.x = 0;
bc1235d4 2796 }
4eeb7d09 2797 wblen++;
2798 *wbptr++ = 0;
32874aea 2799 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2800 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 2801 sfree(workbuf);
bc1235d4 2802}
4eeb7d09 2803
32874aea 2804void term_copyall(void)
2805{
4facdf84 2806 pos top;
2807 top.y = -count234(scrollback);
2808 top.x = 0;
4eeb7d09 2809 clipme(top, curs);
2810}
2811
2812/*
2813 * The wordness array is mainly for deciding the disposition of the US-ASCII
2814 * characters.
2815 */
2816static int wordtype(int uc)
2817{
2818 static struct {
2819 int start, end, ctype;
2820 } *wptr, ucs_words[] = {
2821 {
2822 128, 160, 0}, {
2823 161, 191, 1}, {
2824 215, 215, 1}, {
2825 247, 247, 1}, {
2826 0x037e, 0x037e, 1}, /* Greek question mark */
2827 {
2828 0x0387, 0x0387, 1}, /* Greek ano teleia */
2829 {
2830 0x055a, 0x055f, 1}, /* Armenian punctuation */
2831 {
2832 0x0589, 0x0589, 1}, /* Armenian full stop */
2833 {
2834 0x0700, 0x070d, 1}, /* Syriac punctuation */
2835 {
2836 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2837 {
2838 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2839 {
2840 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2841 {
2842 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2843 {
2844 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2845 {
2846 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2847 {
2848 0x2000, 0x200a, 0}, /* Various spaces */
2849 {
2850 0x2070, 0x207f, 2}, /* superscript */
2851 {
2852 0x2080, 0x208f, 2}, /* subscript */
2853 {
2854 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2855 {
2856 0x3000, 0x3000, 0}, /* ideographic space */
2857 {
2858 0x3001, 0x3020, 1}, /* ideographic punctuation */
2859 {
2860 0x303f, 0x309f, 3}, /* Hiragana */
2861 {
2862 0x30a0, 0x30ff, 3}, /* Katakana */
2863 {
2864 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2865 {
2866 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2867 {
2868 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2869 {
2870 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2871 {
2872 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2873 {
2874 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2875 {
2876 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2877 {
2878 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2879 {
2880 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2881 {
2882 0, 0, 0}
2883 };
2884
2885 uc &= (CSET_MASK | CHAR_MASK);
2886
2887 switch (uc & CSET_MASK) {
2888 case ATTR_LINEDRW:
2889 uc = unitab_xterm[uc & 0xFF];
2890 break;
2891 case ATTR_ASCII:
2892 uc = unitab_line[uc & 0xFF];
2893 break;
2894 }
2895 switch (uc & CSET_MASK) {
2896 case ATTR_ACP:
2897 uc = unitab_font[uc & 0xFF];
2898 break;
2899 case ATTR_OEMCP:
2900 uc = unitab_oemcp[uc & 0xFF];
2901 break;
2902 }
2903
2904 if (uc < 0x80)
2905 return wordness[uc];
2906
2907 for (wptr = ucs_words; wptr->start; wptr++) {
2908 if (uc >= wptr->start && uc <= wptr->end)
2909 return wptr->ctype;
2910 }
2911
2912 return 2;
bc1235d4 2913}
2914
374330e2 2915/*
2916 * Spread the selection outwards according to the selection mode.
2917 */
32874aea 2918static pos sel_spread_half(pos p, int dir)
2919{
4facdf84 2920 unsigned long *ldata;
374330e2 2921 short wvalue;
2922
4facdf84 2923 ldata = lineptr(p.y);
374330e2 2924
2925 switch (selmode) {
2926 case SM_CHAR:
2927 /*
2928 * In this mode, every character is a separate unit, except
2929 * for runs of spaces at the end of a non-wrapping line.
2930 */
4eeb7d09 2931 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 2932 unsigned long *q = ldata + cols;
4facdf84 2933 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 2934 q--;
32874aea 2935 if (q == ldata + cols)
374330e2 2936 q--;
32874aea 2937 if (p.x >= q - ldata)
2938 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 2939 }
2940 break;
2941 case SM_WORD:
2942 /*
2943 * In this mode, the units are maximal runs of characters
2944 * whose `wordness' has the same value.
2945 */
4eeb7d09 2946 wvalue = wordtype(ldata[p.x]);
374330e2 2947 if (dir == +1) {
4eeb7d09 2948 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2949 p.x++;
374330e2 2950 } else {
4eeb7d09 2951 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2952 p.x--;
374330e2 2953 }
2954 break;
2955 case SM_LINE:
2956 /*
2957 * In this mode, every line is a unit.
2958 */
4facdf84 2959 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 2960 break;
2961 }
2962 return p;
2963}
2964
32874aea 2965static void sel_spread(void)
2966{
2967 selstart = sel_spread_half(selstart, -1);
4facdf84 2968 decpos(selend);
32874aea 2969 selend = sel_spread_half(selend, +1);
4facdf84 2970 incpos(selend);
374330e2 2971}
2972
568dd02f 2973void term_do_paste(void)
2974{
2975 wchar_t *data;
2976 int len;
2977
2978 get_clip(&data, &len);
2979 if (data) {
2980 wchar_t *p, *q;
2981
2982 if (paste_buffer)
2983 sfree(paste_buffer);
2984 paste_pos = paste_hold = paste_len = 0;
2985 paste_buffer = smalloc(len * sizeof(wchar_t));
2986
2987 p = q = data;
2988 while (p < data + len) {
2989 while (p < data + len &&
2990 !(p <= data + len - sel_nl_sz &&
2991 !memcmp(p, sel_nl, sizeof(sel_nl))))
2992 p++;
2993
2994 {
2995 int i;
2996 for (i = 0; i < p - q; i++) {
2997 paste_buffer[paste_len++] = q[i];
2998 }
2999 }
3000
3001 if (p <= data + len - sel_nl_sz &&
3002 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3003 paste_buffer[paste_len++] = '\r';
3004 p += sel_nl_sz;
3005 }
3006 q = p;
3007 }
3008
3009 /* Assume a small paste will be OK in one go. */
3010 if (paste_len < 256) {
3011 luni_send(paste_buffer, paste_len);
3012 if (paste_buffer)
3013 sfree(paste_buffer);
3014 paste_buffer = 0;
3015 paste_pos = paste_hold = paste_len = 0;
3016 }
3017 }
3018 get_clip(NULL, NULL);
3019}
3020
32874aea 3021void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3022 int shift, int ctrl)
3023{
4facdf84 3024 pos selpoint;
3025 unsigned long *ldata;
32874aea 3026
3027 if (y < 0)
3028 y = 0;
3029 if (y >= rows)
3030 y = rows - 1;
3031 if (x < 0) {
3032 if (y > 0) {
3033 x = cols - 1;
3034 y--;
3035 } else
3036 x = 0;
094ed2a6 3037 }
32874aea 3038 if (x >= cols)
3039 x = cols - 1;
37508af4 3040
4facdf84 3041 selpoint.y = y + disptop;
3042 selpoint.x = x;
3043 ldata = lineptr(selpoint.y);
32874aea 3044 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 3045 selpoint.x /= 2;
374330e2 3046
01c034ad 3047 if (xterm_mouse) {
3048 int encstate = 0, r, c;
3049 char abuf[16];
3050 static int is_down = 0;
3051
32874aea 3052 switch (b) {
01c034ad 3053 case MBT_LEFT:
32874aea 3054 encstate = 0x20; /* left button down */
01c034ad 3055 break;
3056 case MBT_MIDDLE:
3057 encstate = 0x21;
3058 break;
3059 case MBT_RIGHT:
3060 encstate = 0x22;
3061 break;
3062 case MBT_WHEEL_UP:
3063 encstate = 0x60;
3064 break;
3065 case MBT_WHEEL_DOWN:
3066 encstate = 0x61;
3067 break;
2d466ffd 3068 default: break; /* placate gcc warning about enum use */
01c034ad 3069 }
32874aea 3070 switch (a) {
01c034ad 3071 case MA_DRAG:
3072 if (xterm_mouse == 1)
3073 return;
3074 encstate += 0x20;
3075 break;
3076 case MA_RELEASE:
3077 encstate = 0x23;
3078 is_down = 0;
3079 break;
3080 case MA_CLICK:
3081 if (is_down == b)
3082 return;
3083 is_down = b;
3084 break;
2d466ffd 3085 default: break; /* placate gcc warning about enum use */
01c034ad 3086 }
3087 if (shift)
3088 encstate += 0x04;
3089 if (ctrl)
3090 encstate += 0x10;
3091 r = y + 33;
3092 c = x + 33;
3093
3094 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3095 ldisc_send(abuf, 6);
3096 return;
3097 }
3098
3099 b = translate_button(b);
3100
3101 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3102 deselect();
3103 selstate = ABOUT_TO;
3104 selanchor = selpoint;
3105 selmode = SM_CHAR;
01c034ad 3106 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3107 deselect();
3108 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3109 selstate = DRAGGING;
3110 selstart = selanchor = selpoint;
4facdf84 3111 selend = selstart;
3112 incpos(selend);
374330e2 3113 sel_spread();
01c034ad 3114 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3115 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3116 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3117 return;
01c034ad 3118 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
32874aea 3119 if (posdiff(selpoint, selstart) <
3120 posdiff(selend, selstart) / 2) {
4facdf84 3121 selanchor = selend;
3122 decpos(selanchor);
3123 } else {
374330e2 3124 selanchor = selstart;
4facdf84 3125 }
374330e2 3126 selstate = DRAGGING;
3127 }
3128 if (selstate != ABOUT_TO && selstate != DRAGGING)
3129 selanchor = selpoint;
3130 selstate = DRAGGING;
4facdf84 3131 if (poslt(selpoint, selanchor)) {
374330e2 3132 selstart = selpoint;
4facdf84 3133 selend = selanchor;
3134 incpos(selend);
374330e2 3135 } else {
3136 selstart = selanchor;
4facdf84 3137 selend = selpoint;
a4450583 3138 incpos(selend);
374330e2 3139 }
3140 sel_spread();
01c034ad 3141 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3142 if (selstate == DRAGGING) {
3143 /*
3144 * We've completed a selection. We now transfer the
3145 * data to the clipboard.
3146 */
4eeb7d09 3147 clipme(selstart, selend);
374330e2 3148 selstate = SELECTED;
3149 } else
3150 selstate = NO_SELECTION;
32874aea 3151 } else if (b == MBT_PASTE
3152 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
568dd02f 3153 term_do_paste();
374330e2 3154 }
3155
3156 term_update();
3157}
3158
32874aea 3159void term_nopaste()
3160{
3161 if (paste_len == 0)
3162 return;
c9def1b8 3163 sfree(paste_buffer);
3164 paste_buffer = 0;
3165 paste_len = 0;
3166}
3167
32874aea 3168void term_paste()
3169{
8df7a775 3170 static long last_paste = 0;
c9def1b8 3171 long now, paste_diff;
3172
32874aea 3173 if (paste_len == 0)
3174 return;
c9def1b8 3175
3176 /* Don't wait forever to paste */
32874aea 3177 if (paste_hold) {
3178 now = GetTickCount();
3179 paste_diff = now - last_paste;
3180 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3181 return;
3182 }
3183 paste_hold = 0;
3184
32874aea 3185 while (paste_pos < paste_len) {
8df7a775 3186 int n = 0;
3187 while (n + paste_pos < paste_len) {
3188 if (paste_buffer[paste_pos + n++] == '\r')
3189 break;
3190 }
4eeb7d09 3191 luni_send(paste_buffer + paste_pos, n);
8df7a775 3192 paste_pos += n;
c9def1b8 3193
8df7a775 3194 if (paste_pos < paste_len) {
c9def1b8 3195 paste_hold = 1;
3196 return;
3197 }
3198 }
3199 sfree(paste_buffer);
3200 paste_buffer = 0;
3201 paste_len = 0;
3202}
3203
32874aea 3204static void deselect(void)
3205{
374330e2 3206 selstate = NO_SELECTION;
4facdf84 3207 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3208}
3209
32874aea 3210void term_deselect(void)
3211{
374330e2 3212 deselect();
3213 term_update();
3214}
fe50e814 3215
32874aea 3216int term_ldisc(int option)
3217{
3218 if (option == LD_ECHO)
3219 return term_echoing;
3220 if (option == LD_EDIT)
3221 return term_editing;
0965bee0 3222 return FALSE;
3223}
3224
fe50e814 3225/*
3226 * from_backend(), to get data from the backend for the terminal.
3227 */
32874aea 3228void from_backend(int is_stderr, char *data, int len)
3229{
fe50e814 3230 while (len--) {
3231 if (inbuf_head >= INBUF_SIZE)
3232 term_out();
3233 inbuf[inbuf_head++] = *data++;
3234 }
3235}
e1c8e0ed 3236
3237/*
3238 * Log session traffic.
3239 */
32874aea 3240void logtraffic(unsigned char c, int logmode)
3241{
e1c8e0ed 3242 if (cfg.logtype > 0) {
3243 if (cfg.logtype == logmode) {
3244 /* deferred open file from pgm start? */
32874aea 3245 if (!lgfp)
3246 logfopen();
3247 if (lgfp)
3248 fputc(c, lgfp);
3249 }
e1c8e0ed 3250 }
3251}
3252
3253/* open log file append/overwrite mode */
32874aea 3254void logfopen(void)
3255{
e1c8e0ed 3256 char buf[256];
3257 time_t t;
3258 struct tm *tm;
3259 char writemod[4];
3260
3261 if (!cfg.logtype)
3262 return;
32874aea 3263 sprintf(writemod, "wb"); /* default to rewrite */
3264 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
e1c8e0ed 3265 if (lgfp) {
3266 int i;
3267 fclose(lgfp);
3268 i = askappend(cfg.logfilename);
3269 if (i == 1)
3270 writemod[0] = 'a'; /* set append mode */
3271 else if (i == 0) { /* cancelled */
3272 lgfp = NULL;
32874aea 3273 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3274 return;
3275 }
3276 }
3277
3278 lgfp = fopen(cfg.logfilename, writemod);
32874aea 3279 if (lgfp) { /* enter into event log */
e1c8e0ed 3280 sprintf(buf, "%s session log (%s mode) to file : ",
3281 (writemod[0] == 'a') ? "Appending" : "Writing new",
3282 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3283 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3284 /* Make sure we do not exceed the output buffer size */
32874aea 3285 strncat(buf, cfg.logfilename, 128);
e1c8e0ed 3286 buf[strlen(buf)] = '\0';
3287 logevent(buf);
3288
32874aea 3289 /* --- write header line iinto log file */
3290 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
e1c8e0ed 3291 time(&t);
3292 tm = localtime(&t);
3293 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
32874aea 3294 fputs(buf, lgfp);
3295 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3296 }
3297}
3298
32874aea 3299void logfclose(void)
3300{
3301 if (lgfp) {
3302 fclose(lgfp);
3303 lgfp = NULL;
3304 }
e1c8e0ed 3305}