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